Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
7014572ed8 |
1
.mailmap
@@ -9,4 +9,3 @@ Renato Lorenzi <renato.lorenzi@senior.com.br> Renato.Lorenzi <renato.lore
|
|||||||
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>
|
||||||
csoft2k <csoft2k@hotmail.com>
|
csoft2k <csoft2k@hotmail.com>
|
||||||
Sergio Pulgarin <sergio.pulgarin@gmail.com>
|
|
||||||
|
3
AUTHORS
@@ -12,18 +12,15 @@ Hark
|
|||||||
Joel Lehtonen
|
Joel Lehtonen
|
||||||
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
|
||||||
Patrick Kanzler
|
Patrick Kanzler
|
||||||
Qian Linfeng
|
Qian Linfeng
|
||||||
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
|
||||||
|
@@ -1,55 +1,6 @@
|
|||||||
*********
|
*********
|
||||||
Changelog
|
Changelog
|
||||||
*********
|
*********
|
||||||
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"
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
|
@@ -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
|
||||||
^^^
|
^^^
|
||||||
|
15
README.rst
@@ -6,6 +6,10 @@ python-escpos - Python library to manipulate ESC/POS Printers
|
|||||||
:target: https://travis-ci.org/python-escpos/python-escpos
|
:target: https://travis-ci.org/python-escpos/python-escpos
|
||||||
:alt: Continous Integration
|
:alt: Continous Integration
|
||||||
|
|
||||||
|
.. image:: https://www.quantifiedcode.com/api/v1/project/95748b89a3974700800b85e4ed3d32c4/badge.svg
|
||||||
|
:target: https://www.quantifiedcode.com/app/project/95748b89a3974700800b85e4ed3d32c4
|
||||||
|
:alt: Code issues
|
||||||
|
|
||||||
.. image:: https://landscape.io/github/python-escpos/python-escpos/master/landscape.svg?style=flat
|
.. image:: https://landscape.io/github/python-escpos/python-escpos/master/landscape.svg?style=flat
|
||||||
:target: https://landscape.io/github/python-escpos/python-escpos/master
|
:target: https://landscape.io/github/python-escpos/python-escpos/master
|
||||||
:alt: Code Health
|
:alt: Code Health
|
||||||
@@ -70,13 +74,4 @@ The full project-documentation is available on `Read the Docs <https://python-es
|
|||||||
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
@@ -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
@@ -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
@@ -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
|
||||||
|
)
|
@@ -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"
|
||||||
|
|
||||||
|
@@ -5,4 +5,3 @@ pyserial
|
|||||||
sphinx-rtd-theme
|
sphinx-rtd-theme
|
||||||
setuptools-scm
|
setuptools-scm
|
||||||
docutils>=0.12
|
docutils>=0.12
|
||||||
viivakoodi
|
|
||||||
|
@@ -1,11 +0,0 @@
|
|||||||
from escpos.printer import Usb
|
|
||||||
|
|
||||||
|
|
||||||
# Adapt to your needs
|
|
||||||
p = Usb(0x0416, 0x5011, profile="POS-5890")
|
|
||||||
|
|
||||||
# Print software and then hardware barcode with the same content
|
|
||||||
p.soft_barcode('code39', '123456')
|
|
||||||
p.text('\n')
|
|
||||||
p.text('\n')
|
|
||||||
p.barcode('123456', 'CODE39')
|
|
@@ -37,14 +37,14 @@ def print_codepage(printer, codepage):
|
|||||||
sep = ""
|
sep = ""
|
||||||
|
|
||||||
# Table header
|
# Table header
|
||||||
printer.set(font='b')
|
printer.set(text_type='B')
|
||||||
printer._raw(" {}\n".format(sep.join(map(lambda s: hex(s)[2:], range(0, 16)))))
|
printer._raw(" {}\n".format(sep.join(map(lambda s: hex(s)[2:], range(0, 16)))))
|
||||||
printer.set()
|
printer.set()
|
||||||
|
|
||||||
# The table
|
# The table
|
||||||
for x in range(0, 16):
|
for x in range(0, 16):
|
||||||
# First column
|
# First column
|
||||||
printer.set(font='b')
|
printer.set(text_type='B')
|
||||||
printer._raw("{} ".format(hex(x)[2:]))
|
printer._raw("{} ".format(hex(x)[2:]))
|
||||||
printer.set()
|
printer.set()
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 5.9 KiB |
Before Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 4.7 KiB |
@@ -1,10 +0,0 @@
|
|||||||
# Climacons by Adam Whitcroft
|
|
||||||
|
|
||||||
75 climatically categorised pictographs for web and UI design by [@adamwhitcroft](http://www.twitter.com/#!/adamwhitcroft).
|
|
||||||
|
|
||||||
Visit the [Climacons](http://adamwhitcroft.com/climacons/) website for more information.
|
|
||||||
|
|
||||||
Visit [Adam Whitcroft on GitHub](https://github.com/AdamWhitcroft)
|
|
||||||
|
|
||||||
## License
|
|
||||||
You are free to use any of the Climacons Icons (the "icons") in any personal or commercial work without obligation of payment (monetary or otherwise) or attribution, however a credit for the work would be appreciated. **Do not** redistribute or sell and **do not** claim creative credit. Intellectual property rights are not transferred with the download of the icons.
|
|
Before Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 3.1 KiB |
@@ -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)
|
||||||
|
@@ -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")
|
|
5
setup.py
@@ -68,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',
|
||||||
@@ -113,7 +114,7 @@ setup(
|
|||||||
'pyserial',
|
'pyserial',
|
||||||
'six',
|
'six',
|
||||||
'appdirs',
|
'appdirs',
|
||||||
'PyYAML',
|
'pyyaml',
|
||||||
'argparse',
|
'argparse',
|
||||||
'argcomplete',
|
'argcomplete',
|
||||||
'future',
|
'future',
|
||||||
@@ -125,7 +126,7 @@ setup(
|
|||||||
tests_require=[
|
tests_require=[
|
||||||
'jaconv',
|
'jaconv',
|
||||||
'tox',
|
'tox',
|
||||||
'pytest!=3.2.0',
|
'pytest',
|
||||||
'pytest-cov',
|
'pytest-cov',
|
||||||
'pytest-mock',
|
'pytest-mock',
|
||||||
'nose',
|
'nose',
|
||||||
|
@@ -1,47 +1,16 @@
|
|||||||
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
|
||||||
|
|
||||||
|
|
||||||
logging.basicConfig()
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
pickle_dir = environ.get('ESCPOS_CAPABILITIES_PICKLE_DIR', '/tmp/')
|
|
||||||
pickle_path = path.join(pickle_dir, 'capabilities.pickle')
|
|
||||||
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:
|
|
||||||
full_load = False
|
|
||||||
logger.debug('Loading capabilities from pickle in %s', pickle_path)
|
|
||||||
with open(pickle_path, 'rb') as cf:
|
|
||||||
CAPABILITIES = pickle.load(cf)
|
|
||||||
else:
|
else:
|
||||||
logger.debug('Capabilities pickle file not found: %s', pickle_path)
|
file_path = path.join(path.dirname(__file__), 'capabilities.json')
|
||||||
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.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']
|
||||||
|
|
||||||
|
@@ -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
|
|
||||||
|
@@ -19,14 +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
|
||||||
|
|
||||||
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
|
||||||
@@ -37,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
|
||||||
@@ -81,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.
|
||||||
@@ -109,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
|
||||||
@@ -179,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):
|
|
||||||
""" 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.
|
||||||
@@ -192,7 +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
|
|
||||||
"""
|
"""
|
||||||
# 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]:
|
||||||
@@ -219,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)
|
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.
|
||||||
@@ -264,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)
|
||||||
@@ -288,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:func`~escpos.Escpos.barcode`
|
|
||||||
:param code: alphanumeric data to be printed as bar code, see :py:func`~escpos.Escpos.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
|
||||||
@@ -391,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:func:`~escpos.Escpos.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`
|
||||||
@@ -417,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'])
|
||||||
@@ -509,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
|
||||||
|
|
||||||
@@ -826,40 +754,19 @@ 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.
|
||||||
: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)"""
|
:rtype: array(integer)"""
|
||||||
self._raw(mode)
|
self._raw(RT_STATUS_ONLINE)
|
||||||
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 printer its online status.
|
||||||
When online, returns True; False otherwise.
|
When online, returns True; False otherwise.
|
||||||
:rtype: bool: True if online, False if offline."""
|
:rtype: bool: True if online, False if offline."""
|
||||||
status = self.query_status(RT_STATUS_ONLINE)
|
return not (self.query_status()[0] & RT_MASK_ONLINE)
|
||||||
if len(status) == 0:
|
|
||||||
return False
|
|
||||||
return not (status & RT_MASK_ONLINE)
|
|
||||||
|
|
||||||
def paper_status(self):
|
|
||||||
""" Queries the printer its paper status.
|
|
||||||
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.
|
|
||||||
:rtype: int: 2: Paper is adequate. 1: Paper ending. 0: No paper."""
|
|
||||||
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):
|
||||||
|
@@ -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=""):
|
||||||
|
@@ -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
|
|
||||||
|
@@ -155,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:
|
||||||
@@ -243,7 +239,7 @@ class File(Escpos):
|
|||||||
def __init__(self, devfile="/dev/usb/lp0", auto_flush=True, *args, **kwargs):
|
def __init__(self, devfile="/dev/usb/lp0", auto_flush=True, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
:param devfile: Device file under dev filesystem
|
: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)
|
||||||
|
@@ -7,7 +7,7 @@ from __future__ import unicode_literals
|
|||||||
import escpos.printer as printer
|
import escpos.printer as printer
|
||||||
from escpos.constants import BARCODE_TYPE_A, BARCODE_TYPE_B
|
from escpos.constants import BARCODE_TYPE_A, BARCODE_TYPE_B
|
||||||
from escpos.capabilities import Profile, BARCODE_B
|
from escpos.capabilities import Profile, BARCODE_B
|
||||||
from escpos.exceptions import BarcodeTypeError, BarcodeCodeError
|
from escpos.exceptions import BarcodeTypeError
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@@ -36,17 +36,3 @@ def test_lacks_support(bctype, supports_b):
|
|||||||
instance.barcode('test', bctype)
|
instance.barcode('test', bctype)
|
||||||
|
|
||||||
assert instance.output == b''
|
assert instance.output == b''
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("bctype,data", [
|
|
||||||
('EAN13', 'AA'),
|
|
||||||
('CODE128', '{D2354AA'),
|
|
||||||
])
|
|
||||||
def test_code_check(bctype, data):
|
|
||||||
"""should raise an error if the barcode code is invalid.
|
|
||||||
"""
|
|
||||||
instance = printer.Dummy()
|
|
||||||
with pytest.raises(BarcodeCodeError):
|
|
||||||
instance.barcode(data, bctype)
|
|
||||||
|
|
||||||
assert instance.output == b''
|
|
||||||
|
@@ -1,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))
|
|
@@ -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)
|
|
||||||
|
@@ -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()
|
||||||
@@ -100,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)
|
|
@@ -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)
|
|
||||||
|
@@ -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'
|
|
||||||
|
@@ -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,7 +27,6 @@ else:
|
|||||||
mock_open_call = '__builtin__.open'
|
mock_open_call = '__builtin__.open'
|
||||||
|
|
||||||
|
|
||||||
@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"""
|
||||||
@@ -38,7 +37,6 @@ def test_load_file_printer(mocker, path):
|
|||||||
mock_open.assert_called_with(path, "wb")
|
mock_open.assert_called_with(path, "wb")
|
||||||
|
|
||||||
|
|
||||||
@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"""
|
||||||
@@ -59,7 +57,6 @@ def test_auto_flush(mocker, txt):
|
|||||||
assert mock_device.flush.called
|
assert mock_device.flush.called
|
||||||
|
|
||||||
|
|
||||||
@settings(deadline=None, use_coverage=False)
|
|
||||||
@given(txt=text())
|
@given(txt=text())
|
||||||
def test_flush_on_close(mocker, txt):
|
def test_flush_on_close(mocker, txt):
|
||||||
"""test flush on close in file-printer"""
|
"""test flush on close in file-printer"""
|
||||||
|
@@ -1,32 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
"""test the raising of errors with the error module
|
|
||||||
|
|
||||||
:author: `Patrick Kanzler <patrick.kanzler@fablab.fau.de>`_
|
|
||||||
:organization: `python-escpos <https://github.com/python-escpos>`_
|
|
||||||
:copyright: Copyright (c) 2017 `python-escpos <https://github.com/python-escpos>`_
|
|
||||||
:license: MIT
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import absolute_import
|
|
||||||
from __future__ import division
|
|
||||||
from __future__ import print_function
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
import escpos
|
|
||||||
import escpos.exceptions
|
|
||||||
|
|
||||||
|
|
||||||
def test_raise_error_wrongly():
|
|
||||||
"""raise error the wrong way
|
|
||||||
|
|
||||||
should reproduce https://github.com/python-escpos/python-escpos/issues/257
|
|
||||||
"""
|
|
||||||
with pytest.raises(AttributeError):
|
|
||||||
raise escpos.Error("This should raise an AttributeError.")
|
|
||||||
|
|
||||||
|
|
||||||
def tests_raise_error():
|
|
||||||
"""raise error the right way"""
|
|
||||||
with pytest.raises(escpos.exceptions.Error):
|
|
||||||
raise escpos.exceptions.Error("This should raise an error.")
|
|
4
tox.ini
@@ -7,11 +7,10 @@ deps = nose
|
|||||||
coverage
|
coverage
|
||||||
scripttest
|
scripttest
|
||||||
mock
|
mock
|
||||||
pytest!=3.2.0
|
pytest
|
||||||
pytest-cov
|
pytest-cov
|
||||||
pytest-mock
|
pytest-mock
|
||||||
hypothesis
|
hypothesis
|
||||||
viivakoodi
|
|
||||||
commands = py.test --cov escpos
|
commands = py.test --cov escpos
|
||||||
|
|
||||||
[testenv:docs]
|
[testenv:docs]
|
||||||
@@ -19,7 +18,6 @@ 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]
|
||||||
|