1
0
mirror of https://github.com/python-escpos/python-escpos synced 2025-08-24 09:03:34 +00:00

Merge branch 'capabilities' into text-encoding

This commit is contained in:
Michael Elsdörfer
2016-08-26 15:28:29 +02:00
15 changed files with 507 additions and 12 deletions

View File

@@ -0,0 +1,83 @@
import re
from os import path
import yaml
with open(path.join(path.dirname(__file__), 'capabilities.yml')) as f:
PROFILES = yaml.load(f)
class Profile(object):
profile_data = {}
def __init__(self, columns=None):
self.default_columns = columns
def __getattr__(self, name):
return self.profile_data[name]
def get_columns(self, font):
""" Return the number of columns for the given font.
"""
if self.default_columns:
return self.default_columns
if 'columnConfigs' in self.profile_data:
columns_def = self.columnConfigs[self.defaultColumnConfig]
elif 'columns' in self.profile_data:
columns_def = self.columns
if isinstance(columns_def, int):
return columns_def
return columns_def[font]
def get_profile(name=None, **kwargs):
if isinstance(name, Profile):
return name
clazz = get_profile_class(name or 'default')
return clazz(**kwargs)
CLASS_CACHE = {}
def get_profile_class(name):
if not name in CLASS_CACHE:
profile_data = resolve_profile_data(name)
class_name = '%sProfile' % clean(name)
new_class = type(class_name, (Profile,), {'profile_data': profile_data})
CLASS_CACHE[name] = new_class
return CLASS_CACHE[name]
def clean(s):
# Remove invalid characters
s = re.sub('[^0-9a-zA-Z_]', '', s)
# Remove leading characters until we find a letter or underscore
s = re.sub('^[^a-zA-Z_]+', '', s)
return str(s)
def resolve_profile_data(name):
data = PROFILES[name]
inherits = data.get('inherits')
if not inherits:
return data
if not isinstance(inherits, (tuple, list)):
inherits = [inherits]
merged = {}
for base in reversed(inherits):
base_data = resolve_profile_data(base)
merged.update(base_data)
merged.update(data)
return merged

207
src/escpos/capabilities.yml Normal file
View File

@@ -0,0 +1,207 @@
# Description of the format
abstract:
# Defines non-standard code pages that the printer supports, but
# that we won't find in Python's encoding system. If you define one
# here, don't forget to add it to codePageMap to assign it to a slot.
customCodePages:
sample:
# This maps the indexed code page slots to code page names.
# Often, the slot assignment is the same, but the device only
# supports a subset.
codePageMap:
0: "CP437"
1: "CP932"
3: "sample"
# Maybe not all of the codepages in the map are supported. This
# is for subprofiles to select which ones the device knows.
codePages: [sample, cp932]
# Many recent Epson-branded thermal receipt printers.
default:
columns: 42
barcodeB: true
bitImage: true
graphics: true
starCommands: false
qrCode: true
customCodePages:
TCVN-3-1: [
" ",
" ",
" ăâêôơưđ ",
" àảãáạ ằẳẵắ ",
" ặầẩẫấậè ẻẽ",
"éẹềểễếệìỉ ĩíịò",
" ỏõóọồổỗốộờởỡớợù",
" ủũúụừửữứựỳỷỹýỵ ",
]
TCVN-3-2: [
" ",
" ",
" ĂÂ Ð ÊÔƠƯ ",
" ÀẢÃÁẠ ẰẲẴẮ ",
" ẶẦẨẪẤẬÈ ẺẼ",
"ÉẸỀỂỄẾỆÌỈ ĨÍỊÒ",
" ỎÕÓỌỒỔỖỐỘỜỞỠỚỢÙ",
" ỦŨÚỤỪỬỮỨỰỲỶỸÝỴ "
]
# Commented-out slots are TODO (might just need uncomment, might
# need verification/research)
codePageMap:
0: "CP437"
1: "CP932"
2: "CP850"
3: "CP860"
4: "CP863"
5: "CP865"
#6: // Hiragana
#7: // One-pass printing Kanji characters
#8: // Page 8 [One-pass printing Kanji characters]
11: "CP851"
12: "CP853"
13: "CP857"
14: "CP737"
15: "ISO8859_7"
16: "CP1252"
17: "CP866"
18: "CP852"
19: "CP858"
#20: // Thai Character Code 42
#21: // Thai Character Code 1"
#22: // Thai Character Code 13
#23: // Thai Character Code 14
#24: // Thai Character Code 16
#25: // Thai Character Code 17
#26: // Thai Character Code 18
30: 'TCVN-3-1', # TCVN-3: Vietnamese
31: 'TCVN-3-2', # TCVN-3: Vietnamese
32: "CP720"
33: "CP775"
34: "CP855"
35: "CP861"
36: "CP862"
37: "CP864"
38: "CP869"
39: "ISO8859_2"
40: "ISO8859_15"
41: "CP1098"
42: "CP774"
43: "CP772"
44: "CP1125"
45: "CP1250"
46: "CP1251"
47: "CP1253"
48: "CP1254"
49: "CP1255"
50: "CP1256"
51: "CP1257"
52: "CP1258"
53: "RK1048"
#66: // Devanagari
#67: // Bengali
#68: // Tamil
#69: // Telugu
#70: // Assamese
#71: // Oriya
#72: // Kannada
#73: // Malayalam
#74: // Gujarati
#75: // Punjabi
#82: // Marathi
#254:
#255:
# Designed for non-Epson printers sold online. Without knowing
# their character encoding table, only CP437 output is assumed,
# and graphics() calls will be disabled, as it usually prints junk
# on these models.
simple:
codePages:
- cp437
graphics: false
# Profile for Star-branded printers.
star:
inherits: default
starCommands: true
epson:
inherits: default
manufacturer: "Epson"
"P-822D":
inherits: default
graphics: false
# http://support.epostraders.co.uk/support-files/documents/3/l7O-TM-T88II_TechnicalRefGuide.pdf
"TM-T88II":
inherits: epson
columns:
a: 42
b: 56
codePages:
- PC437 # 0
- Katakana # 1
- PC850 # 2
- PC860 # 3
- PC863 # 4
- PC865 # 5
- PC858 # 19
- blank
# http://support.epostraders.co.uk/support-files/documents/3/l7O-TM-T88II_TechnicalRefGuide.pdf
"TM-T88III":
inherits: epson
columns:
a: 42
b: 56
codePages:
- PC437 # 0
- Katakana # 1
- PC850 # 2
- PC860 # 3
- PC863 # 4
- PC865 # 5
- WPC1252 # 16
- PC866 # 17
- PC852 # 18
- PC858 # 19
- blank
"TM-P80":
inherits: epson
defaultColumnConfig: default
columnConfigs:
default: {'a': 48, 'b': 64, 'kanji': 24}
'42_emulation': {'a': 42, 'b': 60, 'kanji': 21}
"TM-P60II 2":
inherits: epson
columnConfigs:
'58mm_paper': {'a': 35, 'b': 42, 'c': 52}
'60mm_paper': {'a': 36, 'b': 43, 'c': 54}
"TM-P20 2":
inherits: epson
# Has 5 fonts!
"TM-T90":
inherits: epson
colors:
- black
- red

View File

@@ -240,10 +240,7 @@ BARCODE_TYPE_B = {
'NW7': _SET_BARCODE_TYPE(71),
'CODABAR': _SET_BARCODE_TYPE(71), # Same as NW7
'CODE93': _SET_BARCODE_TYPE(72),
# These are all the same barcode, but using different charcter sets
'CODE128A': _SET_BARCODE_TYPE(73) + b'{A', # CODE128 character set A
'CODE128B': _SET_BARCODE_TYPE(73) + b'{B', # CODE128 character set B
'CODE128C': _SET_BARCODE_TYPE(73) + b'{C', # CODE128 character set C
'CODE128': _SET_BARCODE_TYPE(73),
'GS1-128': _SET_BARCODE_TYPE(74),
'GS1 DATABAR OMNIDIRECTIONAL': _SET_BARCODE_TYPE(75),
'GS1 DATABAR TRUNCATED': _SET_BARCODE_TYPE(76),

View File

@@ -24,6 +24,7 @@ from .magicencode import MagicEncode
from abc import ABCMeta, abstractmethod # abstract base class support
from escpos.image import EscposImage
from escpos.capabilities import get_profile
@six.add_metaclass(ABCMeta)
@@ -35,11 +36,11 @@ class Escpos(object):
"""
device = None
def __init__(self, columns=32, **kwargs):
def __init__(self, profile=None, **kwargs):
""" Initialize ESCPOS Printer
:param columns: Text columns used by the printer. Defaults to 32."""
self.columns = columns
:param profile: Printer profile"""
self.profile = get_profile(profile)
self.magic = MagicEncode(**kwargs)
def __del__(self):
@@ -57,7 +58,8 @@ class Escpos(object):
"""
pass
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=1024):
""" Print an image
You can select whether the printer should print in high density or not. The default value is high density.
@@ -77,9 +79,20 @@ class Escpos(object):
:param high_density_vertical: print in high density in vertical 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 fragment_height: Images larger than this will be split into multiple fragments *default:* 1024
"""
im = EscposImage(img_source)
if im.height > fragment_height:
fragments = im.split(fragment_height)
for fragment in fragments:
self.image(fragment,
high_density_vertical=high_density_vertical,
high_density_horizontal=high_density_horizontal,
impl=impl,
fragment_height=fragment_height)
return
if impl == "bitImageRaster":
# GS v 0, raster format bit image
@@ -362,7 +375,7 @@ class Escpos(object):
txt = six.text_type(txt)
self._raw(self.magic.encode_text(txt=txt))
def block_text(self, txt, columns=None):
def block_text(self, txt, font=None, columns=None):
""" Text is printed wrapped to specified columns
Text has to be encoded in unicode.
@@ -371,7 +384,7 @@ class Escpos(object):
:param columns: amount of columns
:return: None
"""
col_count = self.columns if columns is None else columns
col_count = self.profile.get_columns(font) if columns is None else columns
self.text(textwrap.fill(txt, col_count))
def set(self, align='left', font='a', text_type='normal', width=1, height=1, density=9, invert=False, smooth=False,

View File

@@ -8,6 +8,12 @@ This module contains the image format handler :py:class:`EscposImage`.
:license: GNU GPL v3
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import math
from PIL import Image, ImageOps
@@ -30,6 +36,9 @@ class EscposImage(object):
else:
img_original = Image.open(img_source)
# store image for eventual further processing (splitting)
self.img_original = img_original
# Convert to white RGB background, paste over white background
# to strip alpha.
img_original = img_original.convert('RGBA')
@@ -88,3 +97,21 @@ class EscposImage(object):
Convert image to raster-format binary
"""
return self._im.tobytes()
def split(self, fragment_height):
"""
Split an image into multiple fragments after fragment_height pixels
:param fragment_height: height of fragment
:return: list of PIL objects
"""
passes = int(math.ceil(self.height/fragment_height))
fragments = []
for n in range(0, passes):
left = 0
right = self.width
upper = n * fragment_height
lower = min((n + 1) * fragment_height, self.height)
box = (left, upper, right, lower)
fragments.append(self.img_original.crop(box))
return fragments