mirror of
https://github.com/python-escpos/python-escpos
synced 2025-06-25 08:38:43 +00:00
Compare commits
No commits in common. "master" and "v3.1" have entirely different histories.
2
.github/workflows/documentation.yml
vendored
2
.github/workflows/documentation.yml
vendored
@ -27,6 +27,6 @@ jobs:
|
|||||||
sudo apt-get update -y &&
|
sudo apt-get update -y &&
|
||||||
sudo apt-get install -y git python3-sphinx graphviz libenchant-2-2 &&
|
sudo apt-get install -y git python3-sphinx graphviz libenchant-2-2 &&
|
||||||
sudo apt-get install -y gcc libcups2-dev python3-dev python3-setuptools &&
|
sudo apt-get install -y gcc libcups2-dev python3-dev python3-setuptools &&
|
||||||
sudo pip install --ignore-installed tox pycups
|
sudo pip install tox pycups
|
||||||
- name: Test doc build
|
- name: Test doc build
|
||||||
run: tox -e docs
|
run: tox -e docs
|
||||||
|
10
.github/workflows/pythonpackage-windows.yml
vendored
10
.github/workflows/pythonpackage-windows.yml
vendored
@ -19,7 +19,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
submodules: 'recursive'
|
submodules: 'recursive'
|
||||||
- name: Set up Python ${{ matrix.python-version }}
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
uses: actions/setup-python@v5.6.0
|
uses: actions/setup-python@v5.0.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
@ -44,14 +44,12 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
ESCPOS_CAPABILITIES_FILE: D:\a\python-escpos\python-escpos\capabilities-data\dist\capabilities.json
|
ESCPOS_CAPABILITIES_FILE: D:\a\python-escpos\python-escpos\capabilities-data\dist\capabilities.json
|
||||||
- name: Upload coverage to Codecov
|
- name: Upload coverage to Codecov
|
||||||
uses: codecov/codecov-action@v5
|
uses: codecov/codecov-action@v3
|
||||||
env:
|
|
||||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
|
||||||
with:
|
with:
|
||||||
|
directory: ./coverage/reports/
|
||||||
env_vars: OS,PYTHON
|
env_vars: OS,PYTHON
|
||||||
fail_ci_if_error: true
|
fail_ci_if_error: true
|
||||||
files: ./coverage.xml
|
files: ./coverage.xml,!./cache
|
||||||
exclude: "**/.mypy_cache"
|
|
||||||
flags: unittests
|
flags: unittests
|
||||||
name: coverage-tox-${{ matrix.python-version }}
|
name: coverage-tox-${{ matrix.python-version }}
|
||||||
verbose: true
|
verbose: true
|
||||||
|
10
.github/workflows/pythonpackage.yml
vendored
10
.github/workflows/pythonpackage.yml
vendored
@ -22,7 +22,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
submodules: 'recursive'
|
submodules: 'recursive'
|
||||||
- name: Set up Python ${{ matrix.python-version }}
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
uses: actions/setup-python@v5.6.0
|
uses: actions/setup-python@v5.0.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
@ -54,14 +54,12 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
ESCPOS_CAPABILITIES_FILE: /home/runner/work/python-escpos/python-escpos/capabilities-data/dist/capabilities.json
|
ESCPOS_CAPABILITIES_FILE: /home/runner/work/python-escpos/python-escpos/capabilities-data/dist/capabilities.json
|
||||||
- name: Upload coverage to Codecov
|
- name: Upload coverage to Codecov
|
||||||
uses: codecov/codecov-action@v5
|
uses: codecov/codecov-action@v3
|
||||||
env:
|
|
||||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
|
||||||
with:
|
with:
|
||||||
|
directory: ./coverage/reports/
|
||||||
env_vars: OS,PYTHON
|
env_vars: OS,PYTHON
|
||||||
fail_ci_if_error: true
|
fail_ci_if_error: true
|
||||||
files: ./coverage.xml
|
files: ./coverage.xml,!./cache
|
||||||
exclude: "**/.mypy_cache"
|
|
||||||
flags: unittests
|
flags: unittests
|
||||||
name: coverage-tox-${{ matrix.python-version }}
|
name: coverage-tox-${{ matrix.python-version }}
|
||||||
verbose: true
|
verbose: true
|
||||||
|
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@ -12,7 +12,7 @@
|
|||||||
"editor.formatOnPaste": true,
|
"editor.formatOnPaste": true,
|
||||||
"python.formatting.provider": "black",
|
"python.formatting.provider": "black",
|
||||||
"editor.codeActionsOnSave": {
|
"editor.codeActionsOnSave": {
|
||||||
"source.organizeImports": "explicit"
|
"source.organizeImports": true
|
||||||
},
|
},
|
||||||
"python.testing.unittestEnabled": false,
|
"python.testing.unittestEnabled": false,
|
||||||
"python.testing.pytestEnabled": true,
|
"python.testing.pytestEnabled": true,
|
||||||
|
@ -1,18 +1,6 @@
|
|||||||
Changelog
|
Changelog
|
||||||
=========
|
=========
|
||||||
|
|
||||||
202x-xx-xx - Version 3.x - ""
|
|
||||||
-------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
changes
|
|
||||||
^^^^^^^
|
|
||||||
|
|
||||||
|
|
||||||
contributors
|
|
||||||
^^^^^^^^^^^^
|
|
||||||
|
|
||||||
|
|
||||||
2023-12-17 - Version 3.1 - "Rubric Of Ruin"
|
2023-12-17 - Version 3.1 - "Rubric Of Ruin"
|
||||||
-------------------------------------------
|
-------------------------------------------
|
||||||
This is the minor release of the new version 3.1.
|
This is the minor release of the new version 3.1.
|
||||||
@ -69,8 +57,8 @@ changes
|
|||||||
- change the project's license to MIT in accordance with the contributors (see python-escpos/python-escpos#171)
|
- change the project's license to MIT in accordance with the contributors (see python-escpos/python-escpos#171)
|
||||||
- feature: add "capabilities" which are shared with escpos-php, capabilities are stored in
|
- feature: add "capabilities" which are shared with escpos-php, capabilities are stored in
|
||||||
`escpos-printer-db <https://github.com/receipt-print-hq/escpos-printer-db>`_
|
`escpos-printer-db <https://github.com/receipt-print-hq/escpos-printer-db>`_
|
||||||
- feature: the driver tries now to guess the appropriate code page and sets it automatically (called "magic encode")
|
- feature: the driver tries now to guess the appropriate codepage and sets it automatically (called "magic encode")
|
||||||
- as an alternative you can force the code page with the old API
|
- as an alternative you can force the codepage with the old API
|
||||||
- fix the encoding search so that lower encodings are found first
|
- fix the encoding search so that lower encodings are found first
|
||||||
- automatically handle cases where full cut or partial cut is not available
|
- automatically handle cases where full cut or partial cut is not available
|
||||||
- refactor of the set-method
|
- refactor of the set-method
|
||||||
@ -335,14 +323,14 @@ changes
|
|||||||
- change the project's license to MIT in accordance with the contributors (see python-escpos/python-escpos#171)
|
- change the project's license to MIT in accordance with the contributors (see python-escpos/python-escpos#171)
|
||||||
- feature: add "capabilities" which are shared with escpos-php, capabilities are stored in
|
- feature: add "capabilities" which are shared with escpos-php, capabilities are stored in
|
||||||
`escpos-printer-db <https://github.com/receipt-print-hq/escpos-printer-db>`_
|
`escpos-printer-db <https://github.com/receipt-print-hq/escpos-printer-db>`_
|
||||||
- feature: the driver tries now to guess the appropriate code page and sets it automatically (called "magic encode")
|
- feature: the driver tries now to guess the appropriate codepage and sets it automatically (called "magic encode")
|
||||||
- as an alternative you can force the code page with the old API
|
- as an alternative you can force the codepage with the old API
|
||||||
- updated and improved documentation
|
- updated and improved documentation
|
||||||
- changed constructor of main class due to introduction of capabilities
|
- changed constructor of main class due to introduction of capabilities
|
||||||
- changed interface of method `blocktext`, changed behavior of multiple methods, for details refer to the documentation
|
- changed interface of method `blocktext`, changed behavior of multiple methods, for details refer to the documentation
|
||||||
on `python-escpos.readthedocs.io <https://python-escpos.readthedocs.io>`_
|
on `python-escpos.readthedocs.io <https://python-escpos.readthedocs.io>`_
|
||||||
- add support for custom cash drawer sequence
|
- add support for custom cash drawer sequence
|
||||||
- enforce flake8 on the src-files, test py36 and py37 on Travis
|
- enforce flake8 on the src-files, test py36 and py37 on travis
|
||||||
|
|
||||||
contributors
|
contributors
|
||||||
^^^^^^^^^^^^
|
^^^^^^^^^^^^
|
||||||
@ -375,7 +363,7 @@ contributors
|
|||||||
|
|
||||||
changes
|
changes
|
||||||
^^^^^^^
|
^^^^^^^
|
||||||
- configure readthedocs and Travis
|
- configure readthedocs and travis
|
||||||
- update doc with hint on image preprocessing
|
- update doc with hint on image preprocessing
|
||||||
- add fix for printing large images (by splitting them into multiple images)
|
- add fix for printing large images (by splitting them into multiple images)
|
||||||
|
|
||||||
@ -440,8 +428,8 @@ changes
|
|||||||
- improve the documentation
|
- improve the documentation
|
||||||
- extend support of barcode-codes to type B
|
- extend support of barcode-codes to type B
|
||||||
- add function to disable panel-buttons
|
- add function to disable panel-buttons
|
||||||
- the text-functions are now intended for Unicode, the driver will automatically encode the string based on the selected
|
- the text-functions are now intended for unicode, the driver will automatically encode the string based on the selected
|
||||||
code page
|
codepage
|
||||||
- the image-functions are now much more flexible
|
- the image-functions are now much more flexible
|
||||||
- added a CLI
|
- added a CLI
|
||||||
- restructured the constants
|
- restructured the constants
|
||||||
@ -479,7 +467,7 @@ contributors
|
|||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
- Merge pull request #53 from ldos/master
|
- Merge pull request #53 from ldos/master
|
||||||
- Extended parameters for serial printers
|
- Extended params for serial printers
|
||||||
- Sent by ldos <cafeteria.ldosalzira@gmail.com>
|
- Sent by ldos <cafeteria.ldosalzira@gmail.com>
|
||||||
|
|
||||||
2015-04-21 - Version 1.0.5
|
2015-04-21 - Version 1.0.5
|
||||||
|
12
README.rst
12
README.rst
@ -59,7 +59,7 @@ Another example based on the Network printer class:
|
|||||||
|
|
||||||
from escpos.printer import Network
|
from escpos.printer import Network
|
||||||
|
|
||||||
kitchen = Network("192.168.1.100", profile="TM-T88III") #Printer IP Address
|
kitchen = Network("192.168.1.100") #Printer IP Address
|
||||||
kitchen.text("Hello World\n")
|
kitchen.text("Hello World\n")
|
||||||
kitchen.barcode('4006381333931', 'EAN13', 64, 2, '', '')
|
kitchen.barcode('4006381333931', 'EAN13', 64, 2, '', '')
|
||||||
kitchen.cut()
|
kitchen.cut()
|
||||||
@ -71,22 +71,18 @@ Another example based on the Serial printer class:
|
|||||||
from escpos.printer import Serial
|
from escpos.printer import Serial
|
||||||
|
|
||||||
""" 9600 Baud, 8N1, Flow Control Enabled """
|
""" 9600 Baud, 8N1, Flow Control Enabled """
|
||||||
p = Serial(
|
p = Serial(devfile='/dev/tty.usbserial',
|
||||||
devfile='/dev/tty.usbserial',
|
|
||||||
baudrate=9600,
|
baudrate=9600,
|
||||||
bytesize=8,
|
bytesize=8,
|
||||||
parity='N',
|
parity='N',
|
||||||
stopbits=1,
|
stopbits=1,
|
||||||
timeout=1.00,
|
timeout=1.00,
|
||||||
dsrdtr=True,
|
dsrdtr=True)
|
||||||
profile="TM-T88III"
|
|
||||||
)
|
|
||||||
|
|
||||||
p.text("Hello World\n")
|
p.text("Hello World\n")
|
||||||
p.qr("You can readme from your smartphone")
|
p.qr("You can readme from your smartphone")
|
||||||
p.cut()
|
p.cut()
|
||||||
|
|
||||||
.. note:: It is highly recommended to include a matching profile to inform python-escpos about the printer's capabilities.
|
|
||||||
|
|
||||||
The full project-documentation is available on
|
The full project-documentation is available on
|
||||||
`Read the Docs <https://python-escpos.readthedocs.io>`_.
|
`Read the Docs <https://python-escpos.readthedocs.io>`_.
|
||||||
@ -104,4 +100,4 @@ Disclaimer
|
|||||||
|
|
||||||
None of the vendors cited in this project agree or endorse any of the
|
None of the vendors cited in this project agree or endorse any of the
|
||||||
patterns or implementations.
|
patterns or implementations.
|
||||||
Their names are used only to maintain context.
|
Its names are used only to maintain context.
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit e3bf6056ee75cf70ffaccb925081fffa7ad6ced5
|
Subproject commit 4006299c0fa82bc4d4c297663628346ce3eff6c5
|
13
doc/conf.py
13
doc/conf.py
@ -73,7 +73,7 @@ master_doc = "index"
|
|||||||
|
|
||||||
# General information about the project.
|
# General information about the project.
|
||||||
project = "python-escpos"
|
project = "python-escpos"
|
||||||
copyright = "2024, python-escpos developers"
|
copyright = "2023, python-escpos developers"
|
||||||
|
|
||||||
# The version info for the project you're documenting, acts as replacement for
|
# The version info for the project you're documenting, acts as replacement for
|
||||||
# |version| and |release|, also used in various other places throughout the
|
# |version| and |release|, also used in various other places throughout the
|
||||||
@ -134,6 +134,7 @@ else:
|
|||||||
import sphinx_rtd_theme
|
import sphinx_rtd_theme
|
||||||
|
|
||||||
html_theme = "sphinx_rtd_theme"
|
html_theme = "sphinx_rtd_theme"
|
||||||
|
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
|
||||||
except ImportError:
|
except ImportError:
|
||||||
print("no sphinx_rtd_theme found, switching to nature")
|
print("no sphinx_rtd_theme found, switching to nature")
|
||||||
html_theme = "default"
|
html_theme = "default"
|
||||||
@ -143,14 +144,6 @@ else:
|
|||||||
# documentation.
|
# documentation.
|
||||||
# html_theme_options = {}
|
# html_theme_options = {}
|
||||||
|
|
||||||
# Show a 'Edit on GitHub' link instead of 'View page source'
|
|
||||||
html_context = {
|
|
||||||
"display_github": True,
|
|
||||||
"github_user": "python-escpos",
|
|
||||||
"github_repo": "python-escpos",
|
|
||||||
"github_version": "master/doc/",
|
|
||||||
}
|
|
||||||
|
|
||||||
# Add any paths that contain custom themes here, relative to this directory.
|
# Add any paths that contain custom themes here, relative to this directory.
|
||||||
# html_theme_path = []
|
# html_theme_path = []
|
||||||
|
|
||||||
@ -320,7 +313,7 @@ texinfo_documents = [
|
|||||||
# texinfo_no_detailmenu = False
|
# texinfo_no_detailmenu = False
|
||||||
|
|
||||||
# spellchecker
|
# spellchecker
|
||||||
spelling_ignore_pypi_package_names = False
|
spelling_ignore_pypi_package_names = True
|
||||||
spelling_ignore_wiki_words = True
|
spelling_ignore_wiki_words = True
|
||||||
spelling_ignore_python_builtins = True
|
spelling_ignore_python_builtins = True
|
||||||
spelling_ignore_importable_modules = True
|
spelling_ignore_importable_modules = True
|
||||||
|
@ -5,7 +5,7 @@ Release process
|
|||||||
|
|
||||||
* Update authors file
|
* Update authors file
|
||||||
* Update changelog
|
* Update changelog
|
||||||
* Set annotated tag for release and push to public GitHub
|
* Set annotated tag for release and push to public github
|
||||||
* Build wheel
|
* Build wheel
|
||||||
* Load wheel to PyPi
|
* Load wheel to PyPi
|
||||||
* Prepare project for next release with an empty changelog entry
|
* Prepare project for next release with an empty changelog entry
|
||||||
|
@ -2,11 +2,11 @@ pyusb
|
|||||||
Pillow>=2.0
|
Pillow>=2.0
|
||||||
qrcode>=4.0
|
qrcode>=4.0
|
||||||
pyserial
|
pyserial
|
||||||
sphinx-rtd-theme==3.0.2
|
sphinx-rtd-theme==2.0.0
|
||||||
setuptools
|
setuptools
|
||||||
setuptools-scm
|
setuptools-scm
|
||||||
docutils>=0.12
|
docutils>=0.12
|
||||||
sphinxcontrib-spelling>=8.0.0
|
sphinxcontrib-spelling>=7.2.0
|
||||||
python-barcode>=0.15.0,<1
|
python-barcode>=0.15.0,<1
|
||||||
importlib-metadata
|
importlib-metadata
|
||||||
importlib_resources
|
importlib_resources
|
||||||
|
@ -87,18 +87,13 @@ config
|
|||||||
del
|
del
|
||||||
dev
|
dev
|
||||||
dialout
|
dialout
|
||||||
docstring
|
|
||||||
docstrings
|
docstrings
|
||||||
ean
|
ean
|
||||||
Ean
|
Ean
|
||||||
encodable
|
encodable
|
||||||
Errno
|
|
||||||
fff
|
fff
|
||||||
formatter
|
|
||||||
fullimage
|
fullimage
|
||||||
hw
|
|
||||||
io
|
io
|
||||||
img
|
|
||||||
json
|
json
|
||||||
latin
|
latin
|
||||||
libusb
|
libusb
|
||||||
@ -108,23 +103,16 @@ natively
|
|||||||
php
|
php
|
||||||
pre
|
pre
|
||||||
prefilled
|
prefilled
|
||||||
preprocess
|
|
||||||
preprocessing
|
|
||||||
printcap
|
printcap
|
||||||
programmatically
|
programmatically
|
||||||
py
|
py
|
||||||
pypy
|
pypy
|
||||||
pyserial
|
|
||||||
pyusb
|
|
||||||
pyyaml
|
pyyaml
|
||||||
pywin
|
|
||||||
px
|
px
|
||||||
qrcode
|
qrcode
|
||||||
Raspbian
|
Raspbian
|
||||||
readthedocs
|
|
||||||
rebase
|
rebase
|
||||||
rebased
|
rebased
|
||||||
renderer
|
|
||||||
resetted
|
resetted
|
||||||
rst
|
rst
|
||||||
submodule
|
submodule
|
||||||
@ -133,13 +121,10 @@ src
|
|||||||
testcases
|
testcases
|
||||||
th
|
th
|
||||||
Todo
|
Todo
|
||||||
tox
|
|
||||||
traceback
|
traceback
|
||||||
udev
|
udev
|
||||||
usb
|
usb
|
||||||
USBTimeoutError
|
|
||||||
usec
|
usec
|
||||||
virtualenvs
|
virtualenvs
|
||||||
viivakoodi
|
|
||||||
whitespaces
|
whitespaces
|
||||||
xml
|
xml
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
Usage
|
Usage
|
||||||
=====
|
=====
|
||||||
|
|
||||||
:Last Reviewed: 2025-02-16
|
:Last Reviewed: 2023-08-10
|
||||||
|
|
||||||
Define your printer
|
Define your printer
|
||||||
-------------------
|
-------------------
|
||||||
@ -113,7 +113,7 @@ 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, profile="TM-T88IV")
|
p = printer.Usb(0x04b8,0x0202)
|
||||||
# Print text
|
# Print text
|
||||||
p.text("Hello World\n")
|
p.text("Hello World\n")
|
||||||
# Print image
|
# Print image
|
||||||
@ -142,7 +142,7 @@ format. For windows it is probably at::
|
|||||||
|
|
||||||
%appdata%/python-escpos/config.yaml
|
%appdata%/python-escpos/config.yaml
|
||||||
|
|
||||||
And for Linux::
|
And for linux::
|
||||||
|
|
||||||
$HOME/.config/python-escpos/config.yaml
|
$HOME/.config/python-escpos/config.yaml
|
||||||
|
|
||||||
@ -180,7 +180,6 @@ An example file printer::
|
|||||||
printer:
|
printer:
|
||||||
type: File
|
type: File
|
||||||
devfile: /dev/someprinter
|
devfile: /dev/someprinter
|
||||||
profile: TM-U220
|
|
||||||
|
|
||||||
And for a network printer::
|
And for a network printer::
|
||||||
|
|
||||||
@ -188,7 +187,6 @@ And for a network printer::
|
|||||||
type: Network
|
type: Network
|
||||||
host: 127.0.0.1
|
host: 127.0.0.1
|
||||||
port: 9000
|
port: 9000
|
||||||
profile: TM-U220
|
|
||||||
|
|
||||||
An USB-printer could be defined by::
|
An USB-printer could be defined by::
|
||||||
|
|
||||||
@ -198,32 +196,23 @@ An USB-printer could be defined by::
|
|||||||
idProduct: 0x5678
|
idProduct: 0x5678
|
||||||
in_ep: 0x66
|
in_ep: 0x66
|
||||||
out_ep: 0x01
|
out_ep: 0x01
|
||||||
profile: TM-U220
|
|
||||||
|
|
||||||
Printing text right
|
Printing text right
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
Python-escpos is designed to accept Unicode.
|
Python-escpos is designed to accept unicode.
|
||||||
|
|
||||||
For normal usage you can simply pass your text to the printers ``text()``-function. It will automatically guess
|
For normal usage you can simply pass your text to the printers ``text()``-function. It will automatically guess
|
||||||
the right code page and then send the encoded data to the printer. If this feature does not work, please try to
|
the right codepage and then send the encoded data to the printer. If this feature does not work, please try to
|
||||||
isolate the error and then create an issue on the GitHub project page.
|
isolate the error and then create an issue on the GitHub project page.
|
||||||
|
|
||||||
If you want or need to you can manually set the code page.
|
If you want or need to you can manually set the codepage.
|
||||||
For this please use the ``charcode()``-function.
|
For this please use the ``charcode()``-function.
|
||||||
You can set any key-value that is in ``CHARCODE``.
|
You can set any key-value that is in ``CHARCODE``.
|
||||||
If something is wrong, an ``CharCodeError`` will be raised.
|
If something is wrong, an ``CharCodeError`` will be raised.
|
||||||
After you have manually set the code page the printer won't change it anymore.
|
After you have manually set the codepage the printer won't change it anymore.
|
||||||
You can revert to normal behavior by setting charcode to ``AUTO``.
|
You can revert to normal behavior by setting charcode to ``AUTO``.
|
||||||
|
|
||||||
Resolving bus timeout issues during printing images
|
|
||||||
---------------------------------------------------
|
|
||||||
|
|
||||||
If an error message such as "USBTimeoutError: [Errno 110] Operation timed out" occurs,
|
|
||||||
setting a sleep time between printing fragments can help.
|
|
||||||
|
|
||||||
This can be done with the :meth:`.set_sleep_in_fragment()` method.
|
|
||||||
|
|
||||||
Advanced Usage: Print from binary blob
|
Advanced Usage: Print from binary blob
|
||||||
--------------------------------------
|
--------------------------------------
|
||||||
|
|
||||||
@ -244,7 +233,7 @@ advantage of the fact that `_raw()` accepts binary strings.)
|
|||||||
p._raw(data)
|
p._raw(data)
|
||||||
|
|
||||||
That's all, the printer should then print your data. You can also use this technique to let others reproduce an issue
|
That's all, the printer should then print your data. You can also use this technique to let others reproduce an issue
|
||||||
that you have found. (Just "print" your commands to a File-printer on your local file system.)
|
that you have found. (Just "print" your commands to a File-printer on your local filesystem.)
|
||||||
However, please keep in mind, that often it is easier and better to just supply the code that you are using.
|
However, please keep in mind, that often it is easier and better to just supply the code that you are using.
|
||||||
|
|
||||||
Here you can download an example, that will print a set of common barcodes:
|
Here you can download an example, that will print a set of common barcodes:
|
||||||
@ -253,8 +242,8 @@ Here you can download an example, that will print a set of common barcodes:
|
|||||||
|
|
||||||
.. _advanced-usage-change-capabilities-profile:
|
.. _advanced-usage-change-capabilities-profile:
|
||||||
|
|
||||||
Advanced Usage: change where is the capabilities-profile
|
Advanced Usage: change capabilities-profile
|
||||||
--------------------------------------------------------
|
-------------------------------------------
|
||||||
|
|
||||||
Packaged together with the escpos-code is a capabilities-file. This file in
|
Packaged together with the escpos-code is a capabilities-file. This file in
|
||||||
JSON-format describes the capabilities of different printers. It is developed and hosted in
|
JSON-format describes the capabilities of different printers. It is developed and hosted in
|
||||||
|
@ -33,7 +33,7 @@ def main():
|
|||||||
|
|
||||||
|
|
||||||
def print_codepage(printer, codepage):
|
def print_codepage(printer, codepage):
|
||||||
"""Print a code page."""
|
"""Print a codepage."""
|
||||||
if codepage.isdigit():
|
if codepage.isdigit():
|
||||||
codepage = int(codepage)
|
codepage = int(codepage)
|
||||||
printer._raw(CODEPAGE_CHANGE + bytes((codepage,)))
|
printer._raw(CODEPAGE_CHANGE + bytes((codepage,)))
|
||||||
|
@ -4,9 +4,9 @@ blinker==1.6.2
|
|||||||
click==8.1.3
|
click==8.1.3
|
||||||
Flask==2.3.2
|
Flask==2.3.2
|
||||||
itsdangerous==2.1.2
|
itsdangerous==2.1.2
|
||||||
Jinja2==3.1.6
|
Jinja2==3.1.2
|
||||||
MarkupSafe==2.1.2
|
MarkupSafe==2.1.2
|
||||||
Pillow==10.3.0
|
Pillow==10.0.1
|
||||||
pycups==2.0.1
|
pycups==2.0.1
|
||||||
pypng==0.20220715.0
|
pypng==0.20220715.0
|
||||||
pyserial==3.5
|
pyserial==3.5
|
||||||
@ -17,4 +17,4 @@ PyYAML==6.0
|
|||||||
qrcode==7.4.2
|
qrcode==7.4.2
|
||||||
six==1.16.0
|
six==1.16.0
|
||||||
typing_extensions==4.5.0
|
typing_extensions==4.5.0
|
||||||
Werkzeug==3.0.6
|
Werkzeug==3.0.1
|
@ -1,25 +0,0 @@
|
|||||||
""" Example for software_columns: Print text arranged into columns."""
|
|
||||||
|
|
||||||
from escpos import printer
|
|
||||||
|
|
||||||
p = printer.Dummy(profile="TM-U220")
|
|
||||||
|
|
||||||
font = "a"
|
|
||||||
p.set(font=font)
|
|
||||||
|
|
||||||
# Default: Automatic column width given the characters per line of the printer.
|
|
||||||
text_list = ["col1", "col2", "col3"]
|
|
||||||
charsxline = p.profile.get_columns(font)
|
|
||||||
p.software_columns(text_list=text_list, widths=charsxline, align="center")
|
|
||||||
|
|
||||||
# Tuning some columns:
|
|
||||||
text_list = ["col1", "col2", "col3"]
|
|
||||||
widths = [5, 20] # col1 = 5 chars width, col2 + col3 = 20 chars width
|
|
||||||
align = ["left", "center"] # col1 = left aligned, col2 + col3 = center aligned
|
|
||||||
p.software_columns(text_list=text_list, widths=widths, align=align)
|
|
||||||
|
|
||||||
# Tuning them all:
|
|
||||||
text_list = ["col1", "col2", "col3"]
|
|
||||||
widths = [5, 20, 15]
|
|
||||||
align = ["left", "center", "right"]
|
|
||||||
p.software_columns(text_list=text_list, widths=widths, align=align)
|
|
@ -69,7 +69,7 @@ def forecast(idx):
|
|||||||
printer.text(" high " + str(hi))
|
printer.text(" high " + str(hi))
|
||||||
printer.text(deg)
|
printer.text(deg)
|
||||||
printer.text("\n")
|
printer.text("\n")
|
||||||
# take care of pesky Unicode dash
|
# take care of pesky unicode dash
|
||||||
printer.text(cond.replace("\u2013", "-").encode("utf-8"))
|
printer.text(cond.replace("\u2013", "-").encode("utf-8"))
|
||||||
printer.text("\n \n")
|
printer.text("\n \n")
|
||||||
|
|
||||||
|
@ -75,6 +75,6 @@ all =
|
|||||||
pywin32; platform_system=='Windows'
|
pywin32; platform_system=='Windows'
|
||||||
|
|
||||||
[flake8]
|
[flake8]
|
||||||
exclude = .git,.venv,.tox,.github,.eggs,__pycache__,doc/conf.py,build,dist,capabilities-data,test,src/escpos/constants.py
|
exclude = .git,.tox,.github,.eggs,__pycache__,doc/conf.py,build,dist,capabilities-data,test,src/escpos/constants.py
|
||||||
max-line-length = 120
|
max-line-length = 120
|
||||||
extend-ignore = E203, W503
|
extend-ignore = E203, W503
|
||||||
|
@ -209,38 +209,6 @@ ESCPOS_COMMANDS: List[Dict[str, Any]] = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"parser": {
|
|
||||||
"name": "software_columns",
|
|
||||||
"help": "Print a list of texts arranged into columns",
|
|
||||||
},
|
|
||||||
"defaults": {
|
|
||||||
"func": "software_columns",
|
|
||||||
},
|
|
||||||
"arguments": [
|
|
||||||
{
|
|
||||||
"option_strings": ("--text_list",),
|
|
||||||
"help": "list of texts to print",
|
|
||||||
"nargs": "+",
|
|
||||||
"type": str,
|
|
||||||
"required": True,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"option_strings": ("--widths",),
|
|
||||||
"help": "list of column widths",
|
|
||||||
"nargs": "+",
|
|
||||||
"type": int,
|
|
||||||
"required": True,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"option_strings": ("--align",),
|
|
||||||
"help": "list of column alignments",
|
|
||||||
"nargs": "+",
|
|
||||||
"type": str,
|
|
||||||
"required": True,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"parser": {
|
"parser": {
|
||||||
"name": "cut",
|
"name": "cut",
|
||||||
@ -373,7 +341,7 @@ ESCPOS_COMMANDS: List[Dict[str, Any]] = [
|
|||||||
{
|
{
|
||||||
"option_strings": ("--font",),
|
"option_strings": ("--font",),
|
||||||
"help": "Font choice",
|
"help": "Font choice",
|
||||||
"choices": ["A", "B"],
|
"choices": ["left", "center", "right"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"option_strings": ("--text_type",),
|
"option_strings": ("--text_type",),
|
||||||
@ -522,7 +490,7 @@ def generate_parser() -> argparse.ArgumentParser:
|
|||||||
"""Generate an argparse parser."""
|
"""Generate an argparse parser."""
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description="CLI for python-escpos",
|
description="CLI for python-escpos",
|
||||||
epilog="Printer configuration is defined in the python-escpos configuration "
|
epilog="Printer configuration is defined in the python-escpos config"
|
||||||
"file. See documentation for details.",
|
"file. See documentation for details.",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
"""Helper module for code page handling."""
|
"""Helper module for codepage handling."""
|
||||||
from .capabilities import CAPABILITIES
|
from .capabilities import CAPABILITIES
|
||||||
|
|
||||||
|
|
||||||
@ -9,7 +9,7 @@ class CodePageManager:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, data):
|
def __init__(self, data):
|
||||||
"""Initialize code page manager."""
|
"""Initialize codepage manager."""
|
||||||
self.data = data
|
self.data = data
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -76,18 +76,7 @@ class Config:
|
|||||||
|
|
||||||
if "printer" in config:
|
if "printer" in config:
|
||||||
self._printer_config = config["printer"]
|
self._printer_config = config["printer"]
|
||||||
printer_name = self._printer_config.pop("type")
|
self._printer_name = self._printer_config.pop("type").title()
|
||||||
class_names = {
|
|
||||||
"usb": "Usb",
|
|
||||||
"serial": "Serial",
|
|
||||||
"network": "Network",
|
|
||||||
"file": "File",
|
|
||||||
"dummy": "Dummy",
|
|
||||||
"cupsprinter": "CupsPrinter",
|
|
||||||
"lp": "LP",
|
|
||||||
"win32raw": "Win32Raw",
|
|
||||||
}
|
|
||||||
self._printer_name = class_names.get(printer_name.lower(), printer_name)
|
|
||||||
|
|
||||||
if not self._printer_name or not hasattr(printer, self._printer_name):
|
if not self._printer_name or not hasattr(printer, self._printer_name):
|
||||||
raise exceptions.ConfigSyntaxError(
|
raise exceptions.ConfigSyntaxError(
|
||||||
|
@ -188,8 +188,8 @@ LINESPACING_FUNCS: Dict[int, bytes] = {
|
|||||||
180: ESC + b"3", # line_spacing/180 of an inch, 0 <= line_spacing <= 255
|
180: ESC + b"3", # line_spacing/180 of an inch, 0 <= line_spacing <= 255
|
||||||
}
|
}
|
||||||
|
|
||||||
#: Prefix to change the code page. You need to attach a byte to indicate
|
#: Prefix to change the codepage. You need to attach a byte to indicate
|
||||||
#: the code page to use. We use escpos-printer-db as the data source.
|
#: the codepage to use. We use escpos-printer-db as the data source.
|
||||||
CODEPAGE_CHANGE: bytes = ESC + b"\x74"
|
CODEPAGE_CHANGE: bytes = ESC + b"\x74"
|
||||||
|
|
||||||
# Barcode format
|
# Barcode format
|
||||||
|
@ -12,7 +12,6 @@ This module contains the abstract base class :py:class:`Escpos`.
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import textwrap
|
import textwrap
|
||||||
import time
|
|
||||||
import warnings
|
import warnings
|
||||||
from abc import ABCMeta, abstractmethod # abstract base class support
|
from abc import ABCMeta, abstractmethod # abstract base class support
|
||||||
from re import match as re_match
|
from re import match as re_match
|
||||||
@ -108,8 +107,6 @@ SW_BARCODE_NAMES = {
|
|||||||
for name in barcode.PROVIDED_BARCODES
|
for name in barcode.PROVIDED_BARCODES
|
||||||
}
|
}
|
||||||
|
|
||||||
Alignment = Union[Literal["center", "left", "right"], str]
|
|
||||||
|
|
||||||
|
|
||||||
class Escpos(object, metaclass=ABCMeta):
|
class Escpos(object, metaclass=ABCMeta):
|
||||||
"""ESC/POS Printer object.
|
"""ESC/POS Printer object.
|
||||||
@ -124,9 +121,6 @@ class Escpos(object, metaclass=ABCMeta):
|
|||||||
# object -> The connection object (Usb(), Serial(), Network(), etc.)
|
# object -> The connection object (Usb(), Serial(), Network(), etc.)
|
||||||
_device: Union[Literal[False], Literal[None], object] = False
|
_device: Union[Literal[False], Literal[None], object] = False
|
||||||
|
|
||||||
# sleep time in fragments:
|
|
||||||
_sleep_in_fragment_ms: int = 0
|
|
||||||
|
|
||||||
def __init__(self, profile=None, magic_encode_args=None, **kwargs) -> None:
|
def __init__(self, profile=None, magic_encode_args=None, **kwargs) -> None:
|
||||||
"""Initialize ESCPOS Printer.
|
"""Initialize ESCPOS Printer.
|
||||||
|
|
||||||
@ -182,21 +176,6 @@ class Escpos(object, metaclass=ABCMeta):
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def set_sleep_in_fragment(self, sleep_time_ms: int) -> None:
|
|
||||||
"""Configures the currently active sleep time after sending a fragment.
|
|
||||||
|
|
||||||
If during printing an image an issue like "USBTimeoutError: [Errno 110]
|
|
||||||
Operation timed out" occurs, setting this value to roughly 300
|
|
||||||
milliseconds can help resolve the issue.
|
|
||||||
|
|
||||||
:param sleep_time_ms: sleep time in milliseconds
|
|
||||||
"""
|
|
||||||
self._sleep_in_fragment_ms = sleep_time_ms
|
|
||||||
|
|
||||||
def _sleep_in_fragment(self) -> None:
|
|
||||||
"""Sleeps the preconfigured time after sending a fragment."""
|
|
||||||
time.sleep(self._sleep_in_fragment_ms / 1000)
|
|
||||||
|
|
||||||
def image(
|
def image(
|
||||||
self,
|
self,
|
||||||
img_source,
|
img_source,
|
||||||
@ -265,7 +244,6 @@ class Escpos(object, metaclass=ABCMeta):
|
|||||||
impl=impl,
|
impl=impl,
|
||||||
fragment_height=fragment_height,
|
fragment_height=fragment_height,
|
||||||
)
|
)
|
||||||
self._sleep_in_fragment()
|
|
||||||
return
|
return
|
||||||
|
|
||||||
if impl == "bitImageRaster":
|
if impl == "bitImageRaster":
|
||||||
@ -460,7 +438,7 @@ class Escpos(object, metaclass=ABCMeta):
|
|||||||
Sets the control sequence from ``CHARCODE`` in :py:mod:`escpos.constants` as active.
|
Sets the control sequence from ``CHARCODE`` in :py:mod:`escpos.constants` as active.
|
||||||
It will be sent with the next text sequence.
|
It will be sent with the next text sequence.
|
||||||
If you set the variable code to ``AUTO`` it will try to automatically guess the
|
If you set the variable code to ``AUTO`` it will try to automatically guess the
|
||||||
right code page.
|
right codepage.
|
||||||
(This is the standard behavior.)
|
(This is the standard behavior.)
|
||||||
|
|
||||||
:param code: Name of CharCode
|
:param code: Name of CharCode
|
||||||
@ -876,8 +854,8 @@ class Escpos(object, metaclass=ABCMeta):
|
|||||||
def text(self, txt: str) -> None:
|
def text(self, txt: str) -> None:
|
||||||
"""Print alpha-numeric text.
|
"""Print alpha-numeric text.
|
||||||
|
|
||||||
The text has to be encoded in the currently selected code page.
|
The text has to be encoded in the currently selected codepage.
|
||||||
The input text has to be encoded in Unicode.
|
The input text has to be encoded in unicode.
|
||||||
|
|
||||||
:param txt: text to be printed
|
:param txt: text to be printed
|
||||||
:raises: :py:exc:`~escpos.exceptions.TextError`
|
:raises: :py:exc:`~escpos.exceptions.TextError`
|
||||||
@ -887,8 +865,8 @@ class Escpos(object, metaclass=ABCMeta):
|
|||||||
def textln(self, txt: str = "") -> None:
|
def textln(self, txt: str = "") -> None:
|
||||||
"""Print alpha-numeric text with a newline.
|
"""Print alpha-numeric text with a newline.
|
||||||
|
|
||||||
The text has to be encoded in the currently selected code page.
|
The text has to be encoded in the currently selected codepage.
|
||||||
The input text has to be encoded in Unicode.
|
The input text has to be encoded in unicode.
|
||||||
|
|
||||||
:param txt: text to be printed with a newline
|
:param txt: text to be printed with a newline
|
||||||
:raises: :py:exc:`~escpos.exceptions.TextError`
|
:raises: :py:exc:`~escpos.exceptions.TextError`
|
||||||
@ -909,7 +887,7 @@ class Escpos(object, metaclass=ABCMeta):
|
|||||||
def block_text(self, txt, font="0", columns=None) -> None:
|
def block_text(self, txt, font="0", columns=None) -> None:
|
||||||
"""Print text wrapped to specific columns.
|
"""Print text wrapped to specific 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`
|
||||||
@ -919,115 +897,6 @@ class Escpos(object, metaclass=ABCMeta):
|
|||||||
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))
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _padding(
|
|
||||||
text: str,
|
|
||||||
width: int,
|
|
||||||
align: Alignment = "center",
|
|
||||||
) -> str:
|
|
||||||
"""Add fill space to meet the width.
|
|
||||||
|
|
||||||
The align parameter sets the alignment of the text in space.
|
|
||||||
"""
|
|
||||||
align = align.lower()
|
|
||||||
if align == "center":
|
|
||||||
text = f"{text:^{width}}"
|
|
||||||
elif align == "left":
|
|
||||||
text = f"{text:<{width}}"
|
|
||||||
elif align == "right":
|
|
||||||
text = f"{text:>{width}}"
|
|
||||||
|
|
||||||
return text
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _truncate(text: str, width: int, placeholder: str = ".") -> str:
|
|
||||||
"""Truncate an string at a max width or leave it untouched.
|
|
||||||
|
|
||||||
Add a placeholder at the end of the output text if it has been truncated.
|
|
||||||
"""
|
|
||||||
ph_len = len(placeholder)
|
|
||||||
max_len = width - ph_len
|
|
||||||
return f"{text[:max_len]}{placeholder}" if len(text) > width else text
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _repeat_last(iterable, max_iterations: int = 1000):
|
|
||||||
"""Iterate over the items of a list repeating the last one until max_iterations."""
|
|
||||||
i = 0
|
|
||||||
while i < max_iterations:
|
|
||||||
try:
|
|
||||||
yield iterable[i]
|
|
||||||
except IndexError:
|
|
||||||
yield iterable[-1]
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
def _rearrange_into_cols(self, text_list: list, widths: list[int]) -> list:
|
|
||||||
"""Wrap and convert a list of strings into an array of text columns.
|
|
||||||
|
|
||||||
Set the width of each column by passing a list of widths.
|
|
||||||
Wrap if possible and|or truncate strings longer than its column width.
|
|
||||||
Reorder the wrapped items into an array of text columns.
|
|
||||||
"""
|
|
||||||
n_cols = len(text_list)
|
|
||||||
wrapped = [
|
|
||||||
textwrap.wrap(text, widths[i], break_long_words=False)
|
|
||||||
for i, text in enumerate(text_list)
|
|
||||||
]
|
|
||||||
max_len = max(*[len(text_group) for text_group in wrapped])
|
|
||||||
text_colums = []
|
|
||||||
for i in range(max_len):
|
|
||||||
row = ["" for _ in range(n_cols)]
|
|
||||||
for j, item in enumerate(wrapped):
|
|
||||||
if i in range(len(item)):
|
|
||||||
row[j] = self._truncate(item[i], widths[j])
|
|
||||||
text_colums.append(row)
|
|
||||||
return text_colums
|
|
||||||
|
|
||||||
def _add_padding_into_cols(
|
|
||||||
self,
|
|
||||||
text_list: list[str],
|
|
||||||
widths: list[int],
|
|
||||||
align: list[Alignment],
|
|
||||||
) -> list:
|
|
||||||
"""Add padding, width and alignment into the items of a list of strings."""
|
|
||||||
return [
|
|
||||||
self._padding(text, widths[i], align[i]) for i, text in enumerate(text_list)
|
|
||||||
]
|
|
||||||
|
|
||||||
def software_columns(
|
|
||||||
self,
|
|
||||||
text_list: list,
|
|
||||||
widths: Union[list[int], int],
|
|
||||||
align: Union[list[Alignment], Alignment],
|
|
||||||
) -> None:
|
|
||||||
"""Print a list of strings arranged horizontally in columns.
|
|
||||||
|
|
||||||
:param text_list: list of strings, each item in the list will be printed as a column.
|
|
||||||
|
|
||||||
:param widths: width of each column by passing a list of widths,
|
|
||||||
or a single total width to arrange columns of the same size.
|
|
||||||
If the list of width items is shorter than the list of strings then
|
|
||||||
the last width of the list will be applied till the last string (column).
|
|
||||||
|
|
||||||
:param align: alignment of the text into each column by passing a list of alignments,
|
|
||||||
or a single alignment for all the columns.
|
|
||||||
If the list of alignment items is shorter than the list of strings then
|
|
||||||
the last alignment of the list will be applied till the last string (column).
|
|
||||||
"""
|
|
||||||
n_cols = len(text_list)
|
|
||||||
|
|
||||||
if isinstance(widths, int):
|
|
||||||
widths = [round(widths / n_cols)]
|
|
||||||
widths = list(self._repeat_last(widths, max_iterations=n_cols))
|
|
||||||
|
|
||||||
if isinstance(align, str):
|
|
||||||
align = [align]
|
|
||||||
align = list(self._repeat_last(align, max_iterations=n_cols))
|
|
||||||
|
|
||||||
columns = self._rearrange_into_cols(text_list, widths)
|
|
||||||
for row in columns:
|
|
||||||
padded = self._add_padding_into_cols(row, widths, align)
|
|
||||||
self.textln("".join(padded))
|
|
||||||
|
|
||||||
def set(
|
def set(
|
||||||
self,
|
self,
|
||||||
align: Optional[str] = None,
|
align: Optional[str] = None,
|
||||||
@ -1065,8 +934,8 @@ class Escpos(object, metaclass=ABCMeta):
|
|||||||
:param double_width: doubles the width of the text
|
:param double_width: doubles the width of the text
|
||||||
:param custom_size: uses custom size specified by width and height
|
:param custom_size: uses custom size specified by width and height
|
||||||
parameters. Cannot be used with double_width or double_height.
|
parameters. Cannot be used with double_width or double_height.
|
||||||
:param width: requires custom_size=True, text width multiplier when custom_size is used, decimal range 1-8
|
:param width: text width multiplier when custom_size is used, decimal range 1-8
|
||||||
:param height: requires custom_size=True, text height multiplier when custom_size is used, decimal range 1-8
|
:param height: text height multiplier when custom_size is used, decimal range 1-8
|
||||||
: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
|
:param invert: True enables white on black printing
|
||||||
:param smooth: True enables text smoothing. Effective on 4x4 size text and larger
|
:param smooth: True enables text smoothing. Effective on 4x4 size text and larger
|
||||||
@ -1555,7 +1424,7 @@ class EscposIO:
|
|||||||
f"{text}",
|
f"{text}",
|
||||||
]
|
]
|
||||||
|
|
||||||
# TODO check Unicode handling
|
# TODO check unicode handling
|
||||||
# TODO flush? or on print? (this should prob rather be handled by the _raw-method)
|
# TODO flush? or on print? (this should prob rather be handled by the _raw-method)
|
||||||
for line in lines:
|
for line in lines:
|
||||||
self.printer.set(**params)
|
self.printer.set(**params)
|
||||||
|
@ -4,10 +4,7 @@
|
|||||||
I doubt that this currently works correctly.
|
I doubt that this currently works correctly.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import types
|
|
||||||
import typing
|
|
||||||
|
|
||||||
jaconv: typing.Optional[types.ModuleType]
|
|
||||||
try:
|
try:
|
||||||
import jaconv
|
import jaconv
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"""Magic Encode.
|
"""Magic Encode.
|
||||||
|
|
||||||
This module tries to convert an UTF-8 string to an encoded string for the printer.
|
This module tries to convert an UTF-8 string to an encoded string for the printer.
|
||||||
It uses trial and error in order to guess the right code page.
|
It uses trial and error in order to guess the right codepage.
|
||||||
The code is based on the encoding-code in py-xml-escpos by @fvdsn.
|
The code is based on the encoding-code in py-xml-escpos by @fvdsn.
|
||||||
|
|
||||||
:author: `Patrick Kanzler <dev@pkanzler.de>`_
|
:author: `Patrick Kanzler <dev@pkanzler.de>`_
|
||||||
@ -65,11 +65,11 @@ class Encoder:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_codepage_char_list(encoding):
|
def _get_codepage_char_list(encoding):
|
||||||
"""Get code page character list.
|
"""Get codepage character list.
|
||||||
|
|
||||||
Gets characters 128-255 for a given code page, as an array.
|
Gets characters 128-255 for a given code page, as an array.
|
||||||
|
|
||||||
:param encoding: The name of the encoding. This must appear in the code page list
|
:param encoding: The name of the encoding. This must appear in the CodePage list
|
||||||
"""
|
"""
|
||||||
codepage = CodePages.get_encoding(encoding)
|
codepage = CodePages.get_encoding(encoding)
|
||||||
if "data" in codepage:
|
if "data" in codepage:
|
||||||
@ -91,7 +91,7 @@ class Encoder:
|
|||||||
raise LookupError(f"Can't find a known encoding for {encoding}")
|
raise LookupError(f"Can't find a known encoding for {encoding}")
|
||||||
|
|
||||||
def _get_codepage_char_map(self, encoding):
|
def _get_codepage_char_map(self, encoding):
|
||||||
"""Get code page character map.
|
"""Get codepage character map.
|
||||||
|
|
||||||
Process an encoding and return a map of UTF-characters to code points
|
Process an encoding and return a map of UTF-characters to code points
|
||||||
in this encoding.
|
in this encoding.
|
||||||
@ -166,7 +166,7 @@ class Encoder:
|
|||||||
1. code pages that we already tried before; there is a good
|
1. code pages that we already tried before; there is a good
|
||||||
chance they might work again, reducing the search space,
|
chance they might work again, reducing the search space,
|
||||||
and by re-using already used encodings we might also
|
and by re-using already used encodings we might also
|
||||||
reduce the number of code page change instruction we have
|
reduce the number of codepage change instruction we have
|
||||||
to send. Still, any performance gains will presumably be
|
to send. Still, any performance gains will presumably be
|
||||||
fairly minor.
|
fairly minor.
|
||||||
|
|
||||||
@ -225,7 +225,7 @@ class MagicEncode:
|
|||||||
:param encoding: If you know the current encoding of the printer
|
:param encoding: If you know the current encoding of the printer
|
||||||
when initializing this class, set it here. If the current
|
when initializing this class, set it here. If the current
|
||||||
encoding is unknown, the first character emitted will be a
|
encoding is unknown, the first character emitted will be a
|
||||||
code page switch.
|
codepage switch.
|
||||||
:param disabled:
|
:param disabled:
|
||||||
:param defaultsymbol:
|
:param defaultsymbol:
|
||||||
:param encoder:
|
:param encoder:
|
||||||
@ -284,20 +284,20 @@ class MagicEncode:
|
|||||||
def _handle_character_failed(self, char):
|
def _handle_character_failed(self, char):
|
||||||
"""Write a default symbol.
|
"""Write a default symbol.
|
||||||
|
|
||||||
Called when no code page was found to render a character.
|
Called when no codepage was found to render a character.
|
||||||
"""
|
"""
|
||||||
# Writing the default symbol via write() allows us to avoid
|
# Writing the default symbol via write() allows us to avoid
|
||||||
# unnecesary code page switches.
|
# unnecesary codepage switches.
|
||||||
self.write(self.defaultsymbol)
|
self.write(self.defaultsymbol)
|
||||||
|
|
||||||
def write_with_encoding(self, encoding, text):
|
def write_with_encoding(self, encoding, text):
|
||||||
"""Write the text and inject necessary code page switches."""
|
"""Write the text and inject necessary codepage switches."""
|
||||||
if text is not None and type(text) is not str:
|
if text is not None and type(text) is not str:
|
||||||
raise Error(
|
raise Error(
|
||||||
f"The supplied text has to be Unicode, but is of type {type(text)}."
|
f"The supplied text has to be unicode, but is of type {type(text)}."
|
||||||
)
|
)
|
||||||
|
|
||||||
# We always know the current code page; if the new code page
|
# We always know the current code page; if the new codepage
|
||||||
# is different, emit a change command.
|
# is different, emit a change command.
|
||||||
if encoding != self.encoding:
|
if encoding != self.encoding:
|
||||||
self.encoding = encoding
|
self.encoding = encoding
|
||||||
|
@ -49,8 +49,8 @@ def dependency_pycups(func):
|
|||||||
"""Throw a RuntimeError if pycups is not imported."""
|
"""Throw a RuntimeError if pycups is not imported."""
|
||||||
if not is_usable():
|
if not is_usable():
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"Printing with PyCups requires the pycups library to "
|
"Printing with PyCups requires the pycups library to"
|
||||||
"be installed. Please refer to the documentation on "
|
"be installed. Please refer to the documentation on"
|
||||||
"what to install and install the dependencies for pycups."
|
"what to install and install the dependencies for pycups."
|
||||||
)
|
)
|
||||||
return func(*args, **kwargs)
|
return func(*args, **kwargs)
|
||||||
|
@ -23,7 +23,7 @@ def is_usable() -> bool:
|
|||||||
class File(Escpos):
|
class File(Escpos):
|
||||||
"""Generic file printer.
|
"""Generic file printer.
|
||||||
|
|
||||||
This class is used for parallel port printer or other printers that are directly attached to the file system.
|
This class is used for parallel port printer or other printers that are directly attached to the filesystem.
|
||||||
Note that you should stay away from using USB-to-Parallel-Adapter since they are unreliable
|
Note that you should stay away from using USB-to-Parallel-Adapter since they are unreliable
|
||||||
and produce arbitrary errors.
|
and produce arbitrary errors.
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ class File(Escpos):
|
|||||||
def __init__(self, devfile: str = "", auto_flush: bool = True, *args, **kwargs):
|
def __init__(self, devfile: str = "", auto_flush: bool = True, *args, **kwargs):
|
||||||
"""Initialize file printer with device file.
|
"""Initialize file printer with device file.
|
||||||
|
|
||||||
:param devfile: Device file under dev file system
|
:param devfile: Device file under dev filesystem
|
||||||
:param auto_flush: automatically call flush after every call of _raw()
|
:param auto_flush: automatically call flush after every call of _raw()
|
||||||
"""
|
"""
|
||||||
Escpos.__init__(self, *args, **kwargs)
|
Escpos.__init__(self, *args, **kwargs)
|
||||||
|
@ -34,7 +34,7 @@ def dependency_linux_lp(func):
|
|||||||
"""Throw a RuntimeError if not on a non-Windows system."""
|
"""Throw a RuntimeError if not on a non-Windows system."""
|
||||||
if not is_usable():
|
if not is_usable():
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"This printer driver depends on LP which is not "
|
"This printer driver depends on LP which is not"
|
||||||
"available on Windows systems."
|
"available on Windows systems."
|
||||||
)
|
)
|
||||||
return func(*args, **kwargs)
|
return func(*args, **kwargs)
|
||||||
|
@ -43,8 +43,8 @@ def dependency_pyserial(func):
|
|||||||
"""Throw a RuntimeError if pyserial not installed."""
|
"""Throw a RuntimeError if pyserial not installed."""
|
||||||
if not is_usable():
|
if not is_usable():
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"Printing with Serial requires the pyserial library to "
|
"Printing with Serial requires the pyserial library to"
|
||||||
"be installed. Please refer to the documentation on "
|
"be installed. Please refer to the documentation on"
|
||||||
"what to install and install the dependencies for pyserial."
|
"what to install and install the dependencies for pyserial."
|
||||||
)
|
)
|
||||||
return func(*args, **kwargs)
|
return func(*args, **kwargs)
|
||||||
@ -89,7 +89,7 @@ class Serial(Escpos):
|
|||||||
):
|
):
|
||||||
"""Initialize serial printer.
|
"""Initialize serial printer.
|
||||||
|
|
||||||
:param devfile: Device file under dev file system
|
:param devfile: Device file under dev filesystem
|
||||||
:param baudrate: Baud rate for serial transmission
|
:param baudrate: Baud rate for serial transmission
|
||||||
:param bytesize: Serial buffer size
|
:param bytesize: Serial buffer size
|
||||||
:param timeout: Read/Write timeout
|
:param timeout: Read/Write timeout
|
||||||
|
@ -42,8 +42,8 @@ def dependency_usb(func):
|
|||||||
"""Throw a RuntimeError if usb not installed."""
|
"""Throw a RuntimeError if usb not installed."""
|
||||||
if not is_usable():
|
if not is_usable():
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"Printing with USB connection requires a usb library to "
|
"Printing with USB connection requires a usb library to"
|
||||||
"be installed. Please refer to the documentation on "
|
"be installed. Please refer to the documentation on"
|
||||||
"what to install and install the dependencies for USB."
|
"what to install and install the dependencies for USB."
|
||||||
)
|
)
|
||||||
return func(*args, **kwargs)
|
return func(*args, **kwargs)
|
||||||
|
@ -45,8 +45,8 @@ def dependency_win32print(func):
|
|||||||
"""Throw a RuntimeError if win32print not installed."""
|
"""Throw a RuntimeError if win32print not installed."""
|
||||||
if not is_usable():
|
if not is_usable():
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"Printing with Win32Raw requires a win32print library to "
|
"Printing with Win32Raw requires a win32print library to"
|
||||||
"be installed. Please refer to the documentation on "
|
"be installed. Please refer to the documentation on"
|
||||||
"what to install and install the dependencies for win32print."
|
"what to install and install the dependencies for win32print."
|
||||||
)
|
)
|
||||||
return func(*args, **kwargs)
|
return func(*args, **kwargs)
|
||||||
|
@ -1,80 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""tests for software_columns
|
|
||||||
|
|
||||||
:author: Benito López and the python-escpos developers
|
|
||||||
:organization: `python-escpos <https://github.com/python-escpos>`_
|
|
||||||
:copyright: Copyright (c) 2024 `python-escpos <https://github.com/python-escpos>`_
|
|
||||||
:license: MIT
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
|
|
||||||
def test_rearrange_into_cols(driver) -> None:
|
|
||||||
"""
|
|
||||||
GIVEN a list of columnable text
|
|
||||||
WHEN the column width is different for each column and some strings exceed the max width
|
|
||||||
THEN check the strings are properly wrapped, truncated and rearranged into some columns
|
|
||||||
"""
|
|
||||||
|
|
||||||
output = driver._rearrange_into_cols(
|
|
||||||
text_list=["fits", "row1 row2", "truncate and wrap"], widths=[4, 5, 6]
|
|
||||||
)
|
|
||||||
assert output == [["fits", "row1", "trunc."], ["", "row2", "and"], ["", "", "wrap"]]
|
|
||||||
|
|
||||||
|
|
||||||
def test_add_padding_into_cols(driver) -> None:
|
|
||||||
"""
|
|
||||||
GIVEN a list of strings
|
|
||||||
WHEN adding padding and different alignments to each string
|
|
||||||
THEN check the strings are correctly padded and aligned
|
|
||||||
"""
|
|
||||||
|
|
||||||
output = driver._add_padding_into_cols(
|
|
||||||
text_list=["col1", "col2", "col3"],
|
|
||||||
widths=[6, 6, 6],
|
|
||||||
align=["center", "left", "right"],
|
|
||||||
)
|
|
||||||
assert output == [" col1 ", "col2 ", " col3"]
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("text_list", ["", [], None])
|
|
||||||
@pytest.mark.parametrize("widths", [30.5, "30", None])
|
|
||||||
@pytest.mark.parametrize("align", ["invalid_align_name", "", None])
|
|
||||||
def test_software_columns_invalid_args(driver, text_list, widths, align) -> None:
|
|
||||||
"""
|
|
||||||
GIVEN a dummy printer object
|
|
||||||
WHEN non valid params are passed
|
|
||||||
THEN check raise exception
|
|
||||||
"""
|
|
||||||
bad_text_list = {"text_list": text_list, "widths": 5, "align": "left"}
|
|
||||||
bad_widths = {"text_list": ["valid"], "widths": widths, "align": "left"}
|
|
||||||
bad_align = {"text_list": ["valid"], "widths": 5, "align": align}
|
|
||||||
|
|
||||||
bad_args = [bad_text_list, bad_widths, bad_align]
|
|
||||||
for kwargs in bad_args:
|
|
||||||
with pytest.raises(Exception):
|
|
||||||
driver.software_columns(**kwargs)
|
|
||||||
driver.close()
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"text_list",
|
|
||||||
[
|
|
||||||
["col1", "col2", "col3"],
|
|
||||||
["wrap this string", "wrap this string", "wrap this string"],
|
|
||||||
["truncate_this_string", "truncate_this_string", "truncate_this_string"],
|
|
||||||
],
|
|
||||||
)
|
|
||||||
@pytest.mark.parametrize("widths", [[10, 10, 10], [10], 30])
|
|
||||||
@pytest.mark.parametrize("align", [["center", "left", "right"], ["center"], "center"])
|
|
||||||
def test_software_columns_valid_args(driver, text_list, widths, align) -> None:
|
|
||||||
"""
|
|
||||||
GIVEN a dummy printer object
|
|
||||||
WHEN valid params are passed
|
|
||||||
THEN check no errors
|
|
||||||
"""
|
|
||||||
driver.software_columns(text_list=text_list, widths=widths, align=align)
|
|
||||||
driver.close()
|
|
@ -7,8 +7,7 @@
|
|||||||
:copyright: Copyright (c) 2016 `python-escpos <https://github.com/python-escpos>`_
|
:copyright: Copyright (c) 2016 `python-escpos <https://github.com/python-escpos>`_
|
||||||
:license: MIT
|
:license: MIT
|
||||||
"""
|
"""
|
||||||
import types
|
|
||||||
import typing
|
|
||||||
|
|
||||||
import hypothesis.strategies as st
|
import hypothesis.strategies as st
|
||||||
import pytest
|
import pytest
|
||||||
@ -112,7 +111,6 @@ class TestMagicEncode:
|
|||||||
assert driver.output == b"\x1bt\x00? ist teuro."
|
assert driver.output == b"\x1bt\x00? ist teuro."
|
||||||
|
|
||||||
|
|
||||||
jaconv: typing.Optional[types.ModuleType]
|
|
||||||
try:
|
try:
|
||||||
import jaconv
|
import jaconv
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user