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

1 Commits

Author SHA1 Message Date
Patrick Kanzler
7014572ed8 add appveyor configuration 2017-07-27 16:40:38 +02:00
52 changed files with 622 additions and 972 deletions

1
.gitignore vendored
View File

@@ -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
View File

@@ -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

View File

@@ -8,9 +8,4 @@ Cody (Quantified Code Bot) <cody@quantifiedcode.com> Cody <cody@quantifiedcode.c
Renato Lorenzi <renato.lorenzi@senior.com.br> Renato.Lorenzi <renato.lorenzi@senior.com.br> 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> Ahmed Tahri <nyuubi.10@gmail.com> TAHRI Ahmed <nyuubi.10@gmail.com>
Michael Elsdörfer <michael@elsdoerfer.com> Michael Elsdörfer <michael@elsdoerfer.info> 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> csoft2k <csoft2k@hotmail.com>
Sergio Pulgarin <sergio.pulgarin@gmail.com>
reck31 <rakesh.gunduka@gmail.com>
Alex Debiasio <alex.debiasio@thinkin.io> <alex.debiasio@studenti.unitn.it>
Brian 'Redbeard' Harrington <redbeard@dead-city.org>

View File

@@ -1,36 +1,18 @@
language: python language: python
sudo: false sudo: false
cache: pip cache: pip
dist: xenial
git: git:
depth: 100000 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,31 +21,22 @@ 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 - ./doc/generate_authors.sh --check
@@ -86,4 +59,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"

13
AUTHORS
View File

@@ -1,9 +1,6 @@
Ahmed Tahri Ahmed Tahri
akeonly
Alex Debiasio
Asuki Kono Asuki Kono
belono belono
Brian 'Redbeard' Harrington
Christoph Heuel Christoph Heuel
Cody (Quantified Code Bot) Cody (Quantified Code Bot)
csoft2k csoft2k
@@ -11,29 +8,19 @@ Curtis // mashedkeyboard
Davis Goglin Davis Goglin
Dean Rispin Dean Rispin
Dmytro Katyukha Dmytro Katyukha
Gerard Marull-Paretas
Hark Hark
Joel Lehtonen Joel Lehtonen
Justin Vieira
kennedy
Kristi Kristi
ldos ldos
Lucy Linder
Manuel F Martinez Manuel F Martinez
Michael Billington Michael Billington
Michael Elsdörfer Michael Elsdörfer
mrwunderbar666
Nathan Bookham Nathan Bookham
Omer Akram
Patrick Kanzler Patrick Kanzler
primax79
Qian Linfeng Qian Linfeng
Ramon Poca
reck31
Renato Lorenzi Renato Lorenzi
Romain Porte Romain Porte
Sam Cheng Sam Cheng
Sergio Pulgarin
Stephan Sokolow Stephan Sokolow
Thijs Triemstra Thijs Triemstra
Thomas van den Berg Thomas van den Berg

View File

@@ -1,118 +1,6 @@
********* *********
Changelog Changelog
********* *********
2019-06-19 - Version 3.0a6 - "Mistake not..."
---------------------------------------------
This release is the seventh alpha release of the new version 3.0.
Please be aware the the API is subject to change until v3.0 is
released.
changes
^^^^^^^
- fix inclusion of the capabilities-file
- execute CI jobs also on Windows and macOS-targets
- improve documentation
contributors
^^^^^^^^^^^^
- Patrick Kanzler
2019-06-16 - Version 3.0a5 - "Lightly Seared On The Reality Grill"
------------------------------------------------------------------
This release is the sixth alpha release of the new version 3.0. Please
be aware that the API is subject to change until v3.0 is released.
changes
^^^^^^^
- allow arbitrary USB arguments in USB-class
- add Win32Raw-Printer on Windows-platforms
- add and improve Windows support of USB-class
- use pyyaml safe_load()
- improve doc
- implement _read method of Network printer class
contributors
^^^^^^^^^^^^
- Patrick Kanzler
- Gerard Marull-Paretas
- Ramon Poca
- akeonly
- Omer Akram
- Justin Vieira
2018-05-15 - Version 3.0a4 - "Kakistocrat"
------------------------------------------
This release is the fifth alpha release of the new version 3.0. Please
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"
---------------------------------------- ----------------------------------------

View File

@@ -20,7 +20,6 @@ When contributing the first time, please include a commit with the output of thi
Otherwise the integration-check will fail. Otherwise the integration-check will fail.
When you change your username or mail-address, please also update the `.mailmap` and the authors-list. 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 +47,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
^^^ ^^^

View File

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

View File

@@ -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
@@ -62,34 +66,12 @@ The basic usage is:
p = Usb(0x04b8, 0x0202, 0, profile="TM-T88III") p = Usb(0x04b8, 0x0202, 0, profile="TM-T88III")
p.text("Hello World\n") p.text("Hello World\n")
p.image("logo.gif") p.image("logo.gif")
p.barcode('4006381333931', '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('4006381333931', '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.

125
appveyor.yml Normal file
View File

@@ -0,0 +1,125 @@
environment:
global:
# SDK v7.0 MSVC Express 2008's SetEnv.cmd script will fail if the
# /E:ON and /V:ON options are not enabled in the batch script intepreter
# See: http://stackoverflow.com/a/13751649/163740
CMD_IN_ENV: "cmd /E:ON /V:ON /C .\\appveyor\\run_with_env.cmd"
matrix:
# Python 2.7.10 is the latest version and is not pre-installed.
- PYTHON: "C:\\Python27.10"
PYTHON_VERSION: "2.7.10"
PYTHON_ARCH: "32"
- PYTHON: "C:\\Python27.10-x64"
PYTHON_VERSION: "2.7.10"
PYTHON_ARCH: "64"
# Pre-installed Python versions, which Appveyor may upgrade to
# a later point release.
# See: http://www.appveyor.com/docs/installed-software#python
- PYTHON: "C:\\Python27"
PYTHON_VERSION: "2.7.x" # currently 2.7.9
PYTHON_ARCH: "32"
- PYTHON: "C:\\Python27-x64"
PYTHON_VERSION: "2.7.x" # currently 2.7.9
PYTHON_ARCH: "64"
- PYTHON: "C:\\Python33"
PYTHON_VERSION: "3.3.x" # currently 3.3.5
PYTHON_ARCH: "32"
- PYTHON: "C:\\Python33-x64"
PYTHON_VERSION: "3.3.x" # currently 3.3.5
PYTHON_ARCH: "64"
- PYTHON: "C:\\Python34"
PYTHON_VERSION: "3.4.x" # currently 3.4.3
PYTHON_ARCH: "32"
- PYTHON: "C:\\Python34-x64"
PYTHON_VERSION: "3.4.x" # currently 3.4.3
PYTHON_ARCH: "64"
# Python versions not pre-installed
- PYTHON: "C:\\Python35"
PYTHON_VERSION: "3.5.0"
PYTHON_ARCH: "32"
- PYTHON: "C:\\Python35-x64"
PYTHON_VERSION: "3.5.0"
PYTHON_ARCH: "64"
# Major and minor releases (i.e x.0.0 and x.y.0) prior to 3.3.0 use
# a different naming scheme.
- PYTHON: "C:\\Python270"
PYTHON_VERSION: "2.7.0"
PYTHON_ARCH: "32"
- PYTHON: "C:\\Python270-x64"
PYTHON_VERSION: "2.7.0"
PYTHON_ARCH: "64"
install:
# If there is a newer build queued for the same PR, cancel this one.
# The AppVeyor 'rollout builds' option is supposed to serve the same
# purpose but it is problematic because it tends to cancel builds pushed
# directly to master instead of just PR builds (or the converse).
# credits: JuliaLang developers.
- ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER -ne ((Invoke-RestMethod `
https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | `
Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { `
throw "There are newer queued builds for this pull request, failing early." }
- ECHO "Filesystem root:"
- ps: "ls \"C:/\""
- ECHO "Installed SDKs:"
- ps: "ls \"C:/Program Files/Microsoft SDKs/Windows\""
# Install Python (from the official .msi of http://python.org) and pip when
# not already installed.
- ps: if (-not(Test-Path($env:PYTHON))) { & appveyor\install.ps1 }
# Prepend newly installed Python to the PATH of this build (this cannot be
# done from inside the powershell script as it would require to restart
# the parent CMD process).
- "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
# Check that we have the expected version and architecture for Python
- "python --version"
- "python -c \"import struct; print(struct.calcsize('P') * 8)\""
# Upgrade to the latest version of pip to avoid it displaying warnings
# about it being out of date.
- "pip install --disable-pip-version-check --user --upgrade pip"
# Install the build dependencies of the project. If some dependencies contain
# compiled extensions and are not provided as pre-built wheel packages,
# pip will build them from source using the MSVC compiler matching the
# target Python version and architecture
- "%CMD_IN_ENV% pip install -r requirements.txt"
build_script:
# Build the compiled extension
- "%CMD_IN_ENV% python setup.py build"
test_script:
# Run the project tests
- "%CMD_IN_ENV% python setup.py test"
after_test:
# If tests are successful, create binary packages for the project.
- "%CMD_IN_ENV% python setup.py bdist_wheel"
- "%CMD_IN_ENV% python setup.py bdist_wininst"
- "%CMD_IN_ENV% python setup.py bdist_msi"
- ps: "ls dist"
artifacts:
# Archive the generated packages in the ci.appveyor.com build report.
- path: dist\*

229
appveyor/install.ps1 Normal file
View File

@@ -0,0 +1,229 @@
# Sample script to install Python and pip under Windows
# Authors: Olivier Grisel, Jonathan Helmus, Kyle Kastner, and Alex Willmer
# License: CC0 1.0 Universal: http://creativecommons.org/publicdomain/zero/1.0/
$MINICONDA_URL = "http://repo.continuum.io/miniconda/"
$BASE_URL = "https://www.python.org/ftp/python/"
$GET_PIP_URL = "https://bootstrap.pypa.io/get-pip.py"
$GET_PIP_PATH = "C:\get-pip.py"
$PYTHON_PRERELEASE_REGEX = @"
(?x)
(?<major>\d+)
\.
(?<minor>\d+)
\.
(?<micro>\d+)
(?<prerelease>[a-z]{1,2}\d+)
"@
function Download ($filename, $url) {
$webclient = New-Object System.Net.WebClient
$basedir = $pwd.Path + "\"
$filepath = $basedir + $filename
if (Test-Path $filename) {
Write-Host "Reusing" $filepath
return $filepath
}
# Download and retry up to 3 times in case of network transient errors.
Write-Host "Downloading" $filename "from" $url
$retry_attempts = 2
for ($i = 0; $i -lt $retry_attempts; $i++) {
try {
$webclient.DownloadFile($url, $filepath)
break
}
Catch [Exception]{
Start-Sleep 1
}
}
if (Test-Path $filepath) {
Write-Host "File saved at" $filepath
} else {
# Retry once to get the error message if any at the last try
$webclient.DownloadFile($url, $filepath)
}
return $filepath
}
function ParsePythonVersion ($python_version) {
if ($python_version -match $PYTHON_PRERELEASE_REGEX) {
return ([int]$matches.major, [int]$matches.minor, [int]$matches.micro,
$matches.prerelease)
}
$version_obj = [version]$python_version
return ($version_obj.major, $version_obj.minor, $version_obj.build, "")
}
function DownloadPython ($python_version, $platform_suffix) {
$major, $minor, $micro, $prerelease = ParsePythonVersion $python_version
if (($major -le 2 -and $micro -eq 0) `
-or ($major -eq 3 -and $minor -le 2 -and $micro -eq 0) `
) {
$dir = "$major.$minor"
$python_version = "$major.$minor$prerelease"
} else {
$dir = "$major.$minor.$micro"
}
if ($prerelease) {
if (($major -le 2) `
-or ($major -eq 3 -and $minor -eq 1) `
-or ($major -eq 3 -and $minor -eq 2) `
-or ($major -eq 3 -and $minor -eq 3) `
) {
$dir = "$dir/prev"
}
}
if (($major -le 2) -or ($major -le 3 -and $minor -le 4)) {
$ext = "msi"
if ($platform_suffix) {
$platform_suffix = ".$platform_suffix"
}
} else {
$ext = "exe"
if ($platform_suffix) {
$platform_suffix = "-$platform_suffix"
}
}
$filename = "python-$python_version$platform_suffix.$ext"
$url = "$BASE_URL$dir/$filename"
$filepath = Download $filename $url
return $filepath
}
function InstallPython ($python_version, $architecture, $python_home) {
Write-Host "Installing Python" $python_version "for" $architecture "bit architecture to" $python_home
if (Test-Path $python_home) {
Write-Host $python_home "already exists, skipping."
return $false
}
if ($architecture -eq "32") {
$platform_suffix = ""
} else {
$platform_suffix = "amd64"
}
$installer_path = DownloadPython $python_version $platform_suffix
$installer_ext = [System.IO.Path]::GetExtension($installer_path)
Write-Host "Installing $installer_path to $python_home"
$install_log = $python_home + ".log"
if ($installer_ext -eq '.msi') {
InstallPythonMSI $installer_path $python_home $install_log
} else {
InstallPythonEXE $installer_path $python_home $install_log
}
if (Test-Path $python_home) {
Write-Host "Python $python_version ($architecture) installation complete"
} else {
Write-Host "Failed to install Python in $python_home"
Get-Content -Path $install_log
Exit 1
}
}
function InstallPythonEXE ($exepath, $python_home, $install_log) {
$install_args = "/quiet InstallAllUsers=1 TargetDir=$python_home"
RunCommand $exepath $install_args
}
function InstallPythonMSI ($msipath, $python_home, $install_log) {
$install_args = "/qn /log $install_log /i $msipath TARGETDIR=$python_home"
$uninstall_args = "/qn /x $msipath"
RunCommand "msiexec.exe" $install_args
if (-not(Test-Path $python_home)) {
Write-Host "Python seems to be installed else-where, reinstalling."
RunCommand "msiexec.exe" $uninstall_args
RunCommand "msiexec.exe" $install_args
}
}
function RunCommand ($command, $command_args) {
Write-Host $command $command_args
Start-Process -FilePath $command -ArgumentList $command_args -Wait -Passthru
}
function InstallPip ($python_home) {
$pip_path = $python_home + "\Scripts\pip.exe"
$python_path = $python_home + "\python.exe"
if (-not(Test-Path $pip_path)) {
Write-Host "Installing pip..."
$webclient = New-Object System.Net.WebClient
$webclient.DownloadFile($GET_PIP_URL, $GET_PIP_PATH)
Write-Host "Executing:" $python_path $GET_PIP_PATH
& $python_path $GET_PIP_PATH
} else {
Write-Host "pip already installed."
}
}
function DownloadMiniconda ($python_version, $platform_suffix) {
if ($python_version -eq "3.4") {
$filename = "Miniconda3-3.5.5-Windows-" + $platform_suffix + ".exe"
} else {
$filename = "Miniconda-3.5.5-Windows-" + $platform_suffix + ".exe"
}
$url = $MINICONDA_URL + $filename
$filepath = Download $filename $url
return $filepath
}
function InstallMiniconda ($python_version, $architecture, $python_home) {
Write-Host "Installing Python" $python_version "for" $architecture "bit architecture to" $python_home
if (Test-Path $python_home) {
Write-Host $python_home "already exists, skipping."
return $false
}
if ($architecture -eq "32") {
$platform_suffix = "x86"
} else {
$platform_suffix = "x86_64"
}
$filepath = DownloadMiniconda $python_version $platform_suffix
Write-Host "Installing" $filepath "to" $python_home
$install_log = $python_home + ".log"
$args = "/S /D=$python_home"
Write-Host $filepath $args
Start-Process -FilePath $filepath -ArgumentList $args -Wait -Passthru
if (Test-Path $python_home) {
Write-Host "Python $python_version ($architecture) installation complete"
} else {
Write-Host "Failed to install Python in $python_home"
Get-Content -Path $install_log
Exit 1
}
}
function InstallMinicondaPip ($python_home) {
$pip_path = $python_home + "\Scripts\pip.exe"
$conda_path = $python_home + "\Scripts\conda.exe"
if (-not(Test-Path $pip_path)) {
Write-Host "Installing pip..."
$args = "install --yes pip"
Write-Host $conda_path $args
Start-Process -FilePath "$conda_path" -ArgumentList $args -Wait -Passthru
} else {
Write-Host "pip already installed."
}
}
function main () {
InstallPython $env:PYTHON_VERSION $env:PYTHON_ARCH $env:PYTHON
InstallPip $env:PYTHON
}
main

88
appveyor/run_with_env.cmd Normal file
View File

@@ -0,0 +1,88 @@
:: To build extensions for 64 bit Python 3, we need to configure environment
:: variables to use the MSVC 2010 C++ compilers from GRMSDKX_EN_DVD.iso of:
:: MS Windows SDK for Windows 7 and .NET Framework 4 (SDK v7.1)
::
:: To build extensions for 64 bit Python 2, we need to configure environment
:: variables to use the MSVC 2008 C++ compilers from GRMSDKX_EN_DVD.iso of:
:: MS Windows SDK for Windows 7 and .NET Framework 3.5 (SDK v7.0)
::
:: 32 bit builds, and 64-bit builds for 3.5 and beyond, do not require specific
:: environment configurations.
::
:: Note: this script needs to be run with the /E:ON and /V:ON flags for the
:: cmd interpreter, at least for (SDK v7.0)
::
:: More details at:
:: https://github.com/cython/cython/wiki/64BitCythonExtensionsOnWindows
:: http://stackoverflow.com/a/13751649/163740
::
:: Author: Olivier Grisel
:: License: CC0 1.0 Universal: http://creativecommons.org/publicdomain/zero/1.0/
::
:: Notes about batch files for Python people:
::
:: Quotes in values are literally part of the values:
:: SET FOO="bar"
:: FOO is now five characters long: " b a r "
:: If you don't want quotes, don't include them on the right-hand side.
::
:: The CALL lines at the end of this file look redundant, but if you move them
:: outside of the IF clauses, they do not run properly in the SET_SDK_64==Y
:: case, I don't know why.
@ECHO OFF
SET COMMAND_TO_RUN=%*
SET WIN_SDK_ROOT=C:\Program Files\Microsoft SDKs\Windows
SET WIN_WDK=c:\Program Files (x86)\Windows Kits\10\Include\wdf
:: Extract the major and minor versions, and allow for the minor version to be
:: more than 9. This requires the version number to have two dots in it.
SET MAJOR_PYTHON_VERSION=%PYTHON_VERSION:~0,1%
IF "%PYTHON_VERSION:~3,1%" == "." (
SET MINOR_PYTHON_VERSION=%PYTHON_VERSION:~2,1%
) ELSE (
SET MINOR_PYTHON_VERSION=%PYTHON_VERSION:~2,2%
)
:: Based on the Python version, determine what SDK version to use, and whether
:: to set the SDK for 64-bit.
IF %MAJOR_PYTHON_VERSION% == 2 (
SET WINDOWS_SDK_VERSION="v7.0"
SET SET_SDK_64=Y
) ELSE (
IF %MAJOR_PYTHON_VERSION% == 3 (
SET WINDOWS_SDK_VERSION="v7.1"
IF %MINOR_PYTHON_VERSION% LEQ 4 (
SET SET_SDK_64=Y
) ELSE (
SET SET_SDK_64=N
IF EXIST "%WIN_WDK%" (
:: See: https://connect.microsoft.com/VisualStudio/feedback/details/1610302/
REN "%WIN_WDK%" 0wdf
)
)
) ELSE (
ECHO Unsupported Python version: "%MAJOR_PYTHON_VERSION%"
EXIT 1
)
)
IF %PYTHON_ARCH% == 64 (
IF %SET_SDK_64% == Y (
ECHO Configuring Windows SDK %WINDOWS_SDK_VERSION% for Python %MAJOR_PYTHON_VERSION% on a 64 bit architecture
SET DISTUTILS_USE_SDK=1
SET MSSdk=1
"%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Setup\WindowsSdkVer.exe" -q -version:%WINDOWS_SDK_VERSION%
"%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Bin\SetEnv.cmd" /x64 /release
ECHO Executing: %COMMAND_TO_RUN%
call %COMMAND_TO_RUN% || EXIT 1
) ELSE (
ECHO Using default MSVC build environment for 64 bit architecture
ECHO Executing: %COMMAND_TO_RUN%
call %COMMAND_TO_RUN% || EXIT 1
)
) ELSE (
ECHO Using default MSVC build environment for 32 bit architecture
ECHO Executing: %COMMAND_TO_RUN%
call %COMMAND_TO_RUN% || EXIT 1
)

View File

@@ -1,6 +1,6 @@
#!/bin/sh #!/bin/sh
GENLIST=$(git shortlog -s -n | cut -f2 | sort -f) GENLIST=$(git shortlog -s -n | cut -f2 | sort)
AUTHORSFILE="$(dirname $0)/../AUTHORS" AUTHORSFILE="$(dirname $0)/../AUTHORS"
TEMPAUTHORSFILE="/tmp/python-escpos-authorsfile" TEMPAUTHORSFILE="/tmp/python-escpos-authorsfile"

View File

@@ -5,4 +5,3 @@ pyserial
sphinx-rtd-theme sphinx-rtd-theme
setuptools-scm setuptools-scm
docutils>=0.12 docutils>=0.12
viivakoodi

View File

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

View File

@@ -44,7 +44,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 +55,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 +67,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 +81,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 +93,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 +108,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('4006381333931','EAN13',64,2,'','') Epson.barcode('1324354657687','EAN13',64,2,'','')
# Cut paper # Cut paper
p.cut() Epson.cut()
Configuration File Configuration File
------------------ ------------------

View File

@@ -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')

View File

@@ -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()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

View File

@@ -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.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -16,4 +16,4 @@ if __name__ == '__main__':
# Adapt to your needs # Adapt to your needs
p = Usb(0x0416, 0x5011, profile="POS-5890") p = Usb(0x0416, 0x5011, profile="POS-5890")
p.qr(content, center=True) p.qr(content)

View File

@@ -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")

View File

@@ -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',
@@ -56,7 +85,7 @@ setup(
platforms='any', platforms='any',
package_dir={"": "src"}, package_dir={"": "src"},
packages=find_packages(where="src", exclude=["tests", "tests.*"]), packages=find_packages(where="src", exclude=["tests", "tests.*"]),
package_data={'escpos': ['capabilities.json']}, package_data={'': ['COPYING', 'src/escpos/capabilities.json']},
include_package_data=True, include_package_data=True,
classifiers=[ classifiers=[
'Development Status :: 4 - Beta', 'Development Status :: 4 - Beta',
@@ -65,7 +94,10 @@ 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.6',
@@ -82,7 +114,7 @@ setup(
'pyserial', 'pyserial',
'six', 'six',
'appdirs', 'appdirs',
'PyYAML', 'pyyaml',
'argparse', 'argparse',
'argcomplete', 'argcomplete',
'future', 'future',
@@ -94,15 +126,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'

View File

@@ -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()}

View File

@@ -53,7 +53,7 @@ DEMO_FUNCTIONS = {
'barcodes_a': [ 'barcodes_a': [
{'bc': 'UPC-A', 'code': '13243546576'}, {'bc': 'UPC-A', 'code': '13243546576'},
{'bc': 'UPC-E', 'code': '132435'}, {'bc': 'UPC-E', 'code': '132435'},
{'bc': 'EAN13', 'code': '4006381333931'}, {'bc': 'EAN13', 'code': '1324354657687'},
{'bc': 'EAN8', 'code': '1324354'}, {'bc': 'EAN8', 'code': '1324354'},
{'bc': 'CODE39', 'code': 'TEST'}, {'bc': 'CODE39', 'code': 'TEST'},
{'bc': 'ITF', 'code': '55867492279103'}, {'bc': 'ITF', 'code': '55867492279103'},
@@ -62,13 +62,13 @@ DEMO_FUNCTIONS = {
'barcodes_b': [ 'barcodes_b': [
{'bc': 'UPC-A', 'code': '13243546576', 'function_type': 'B'}, {'bc': 'UPC-A', 'code': '13243546576', 'function_type': 'B'},
{'bc': 'UPC-E', 'code': '132435', 'function_type': 'B'}, {'bc': 'UPC-E', 'code': '132435', 'function_type': 'B'},
{'bc': 'EAN13', 'code': '4006381333931', 'function_type': 'B'}, {'bc': 'EAN13', 'code': '1324354657687', 'function_type': 'B'},
{'bc': 'EAN8', 'code': '1324354', 'function_type': 'B'}, {'bc': 'EAN8', 'code': '1324354', 'function_type': 'B'},
{'bc': 'CODE39', 'code': 'TEST', 'function_type': 'B'}, {'bc': 'CODE39', 'code': 'TEST', 'function_type': 'B'},
{'bc': 'ITF', 'code': '55867492279103', 'function_type': 'B'}, {'bc': 'ITF', 'code': '55867492279103', 'function_type': 'B'},
{'bc': 'NW7', 'code': 'A00000000A', 'function_type': 'B'}, {'bc': 'NW7', 'code': 'A00000000A', 'function_type': 'B'},
{'bc': 'CODE93', 'code': 'A00000000A', 'function_type': 'B'}, {'bc': 'CODE93', 'code': 'A00000000A', 'function_type': 'B'},
{'bc': 'CODE93', 'code': '4006381333931', 'function_type': 'B'}, {'bc': 'CODE93', 'code': '1324354657687', 'function_type': 'B'},
{'bc': 'CODE128A', 'code': 'TEST', 'function_type': 'B'}, {'bc': 'CODE128A', 'code': 'TEST', 'function_type': 'B'},
{'bc': 'CODE128B', 'code': 'TEST', 'function_type': 'B'}, {'bc': 'CODE128B', 'code': 'TEST', 'function_type': 'B'},
{'bc': 'CODE128C', 'code': 'TEST', 'function_type': 'B'}, {'bc': 'CODE128C', 'code': 'TEST', 'function_type': 'B'},

View File

@@ -225,24 +225,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,
@@ -269,10 +251,5 @@ 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 # Status Command
RT_STATUS = DLE + EOT RT_STATUS_ONLINE = DLE + EOT + b'\x01';
RT_STATUS_ONLINE = RT_STATUS + b'\x01' RT_MASK_ONLINE = 8;
RT_STATUS_PAPER = RT_STATUS + b'\x04'
RT_MASK_ONLINE = 8
RT_MASK_PAPER = 18
RT_MASK_LOWPAPER = 30
RT_MASK_NOPAPER = 114

View File

@@ -19,16 +19,13 @@ import qrcode
import textwrap import textwrap
import six import six
import time import time
from re import match as re_match
import barcode import barcode
from barcode.writer import ImageWriter 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 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_SIZE, TXT_NORMAL
from .constants import SET_FONT from .constants import SET_FONT
@@ -39,7 +36,6 @@ 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_CR, CTL_FF, CTL_LF, CTL_SET_HT, PANEL_BUTTON_OFF, PANEL_BUTTON_ON
from .constants import TXT_STYLE from .constants import TXT_STYLE
from .constants import RT_STATUS_ONLINE, RT_MASK_ONLINE 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
@@ -83,14 +79,14 @@ class Escpos(object):
""" """
pass pass
def _read(self): def _read(self, msg):
""" Returns a NotImplementedError if the instance of the class doesn't override this method. """ Returns a NotImplementedError if the instance of the class doesn't override this method.
:raises NotImplementedError :raises NotImplementedError
""" """
raise 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,19 +107,14 @@ 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: try:
max_width = int(self.profile.profile_data['media']['width']['pixels']) max_width = int(self.profile.profile_data['media']['width']['pixels'])
if im.width > max_width: if im.width > max_width:
raise ImageWidthError('{} > {}'.format(im.width, max_width)) raise ImageWidthError('{} > {}'.format(im.width, max_width))
if center:
im.center(max_width)
except KeyError: except KeyError:
# If the printer's pixel width is not known, print anyways... # If the printer's pixel width is not known, print anyways...
pass pass
@@ -181,8 +172,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 +184,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 +210,12 @@ 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.text('\n')
self.image(im, center=center, impl=impl) self.image(im)
self.text('\n') self.text('\n')
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 +250,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 +274,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 +352,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,19 +374,12 @@ 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_STYLE['align']['center'])
@@ -490,8 +437,7 @@ class Escpos(object):
barcode_class = barcode.get_barcode_class(barcode_type) barcode_class = barcode.get_barcode_class(barcode_type)
my_code = barcode_class(data, writer=image_writer) my_code = barcode_class(data, writer=image_writer)
with open(os.devnull, "wb") as nullfile: my_code.write("/dev/null", {
my_code.write(nullfile, {
'module_height': module_height, 'module_height': module_height,
'module_width': module_width, 'module_width': module_width,
'text_distance': text_distance 'text_distance': text_distance
@@ -513,28 +459,6 @@ 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
@@ -701,8 +625,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): def linedisplay_select(self, select_display=False):
""" Selects the line display or the printer """ Selects the line display or the printer
@@ -732,7 +656,6 @@ class Escpos(object):
You should connect a line display to your printer. You can do this by daisy-chaining You should connect a line display to your printer. You can do this by daisy-chaining
the display between your computer and printer. the display between your computer and printer.
:param text: Text to display :param text: Text to display
""" """
self.linedisplay_select(select_display=True) self.linedisplay_select(select_display=True)
@@ -831,58 +754,26 @@ class Escpos(object):
else: else:
self._raw(PANEL_BUTTON_OFF) self._raw(PANEL_BUTTON_OFF)
def query_status(self, mode): def query_status(self):
""" """ Queries the printer for its status, and returns an array of integers containing it.
Queries the printer for its status, and returns an array of integers containing it. :rtype: array(integer)"""
self._raw(RT_STATUS_ONLINE)
: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) time.sleep(1)
status = self._read() status = self._read()
return status return status or [RT_MASK_ONLINE]
def is_online(self): def is_online(self):
""" """ Queries the printer its online status.
Queries the online status of the printer. When online, returns True; False otherwise.
:rtype: bool: True if online, False if offline."""
:returns: When online, returns ``True``; ``False`` otherwise. return not (self.query_status()[0] & RT_MASK_ONLINE)
: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

View File

@@ -77,10 +77,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=""):

View File

@@ -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

View File

@@ -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:
@@ -172,10 +155,6 @@ 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 and self.device.is_open:
@@ -236,11 +215,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 +308,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)

View File

@@ -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''

View File

@@ -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])

View File

@@ -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))

View File

@@ -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'')

View File

@@ -145,8 +145,10 @@ def test_large_graphics():
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 test_width_too_large():
def dummy_with_width(): """
Test printing an image that is too large in width.
"""
instance = printer.Dummy() instance = printer.Dummy()
instance.profile.profile_data = { instance.profile.profile_data = {
'media': { 'media': {
@@ -155,25 +157,8 @@ def dummy_with_width():
} }
} }
} }
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): with pytest.raises(ImageWidthError):
instance.image(Image.new("RGB", (385, 200))) instance.image(Image.new("RGB", (385, 200)))
instance.image(Image.new("RGB", (384, 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)

View File

@@ -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,7 +81,6 @@ 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()
@@ -101,13 +99,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)

View File

@@ -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)

View File

@@ -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")

View File

@@ -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'

View File

@@ -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"""

View File

@@ -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.")

View File

@@ -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]