Match the current printer-db format.
This commit is contained in:
parent
216184f43f
commit
a07f84a5bc
3
setup.py
3
setup.py
|
@ -83,7 +83,8 @@ 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={'': ['COPYING']},
|
package_data={'': ['COPYING', 'src/escpos/capabilities.json']},
|
||||||
|
include_package_data=True,
|
||||||
classifiers=[
|
classifiers=[
|
||||||
'Development Status :: 4 - Beta',
|
'Development Status :: 4 - Beta',
|
||||||
'Environment :: Console',
|
'Environment :: Console',
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,40 +1,53 @@
|
||||||
import re
|
import re
|
||||||
|
import six
|
||||||
from os import path
|
from os import path
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
|
|
||||||
with open(path.join(path.dirname(__file__), 'capabilities.yml')) as f:
|
# Load external printer database
|
||||||
PROFILES = yaml.load(f)
|
with open(path.join(path.dirname(__file__), 'capabilities.json')) as f:
|
||||||
|
CAPABILITIES = yaml.load(f)
|
||||||
|
PROFILES = CAPABILITIES['profiles']
|
||||||
|
ENCODINGS = CAPABILITIES['encodings']
|
||||||
|
|
||||||
|
|
||||||
class Profile(object):
|
class NotSupported(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class BaseProfile(object):
|
||||||
|
"""This respresents a printer profile.
|
||||||
|
|
||||||
|
A printer profile knows about the number of columns, supported
|
||||||
|
features, colors and more.
|
||||||
|
"""
|
||||||
|
|
||||||
profile_data = {}
|
profile_data = {}
|
||||||
|
|
||||||
def __init__(self, columns=None):
|
|
||||||
self.default_columns = columns
|
|
||||||
|
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
return self.profile_data[name]
|
return self.profile_data[name]
|
||||||
|
|
||||||
|
def get_font(self, font):
|
||||||
|
"""Return the escpos index for `font`. Makes sure that
|
||||||
|
the requested `font` is valid.
|
||||||
|
"""
|
||||||
|
font = {'a': 0, 'b': 1}.get(font, font)
|
||||||
|
if not six.text_type(font) in self.fonts:
|
||||||
|
raise NotSupported(
|
||||||
|
'"%s" is not a valid font in the current profile' % font)
|
||||||
|
return font
|
||||||
|
|
||||||
def get_columns(self, font):
|
def get_columns(self, font):
|
||||||
""" Return the number of columns for the given font.
|
""" Return the number of columns for the given font.
|
||||||
"""
|
"""
|
||||||
if self.default_columns:
|
font = self.get_font(font)
|
||||||
return self.default_columns
|
return self.fonts[six.text_type(font)]['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):
|
def get_profile(name=None, **kwargs):
|
||||||
|
"""Get the profile by name; if no name is given, return the
|
||||||
|
default profile.
|
||||||
|
"""
|
||||||
if isinstance(name, Profile):
|
if isinstance(name, Profile):
|
||||||
return name
|
return name
|
||||||
|
|
||||||
|
@ -42,15 +55,19 @@ def get_profile(name=None, **kwargs):
|
||||||
return clazz(**kwargs)
|
return clazz(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
CLASS_CACHE = {}
|
CLASS_CACHE = {}
|
||||||
|
|
||||||
|
|
||||||
def get_profile_class(name):
|
def get_profile_class(name):
|
||||||
|
"""For the given profile name, load the data from the external
|
||||||
|
database, then generate dynamically a class.
|
||||||
|
"""
|
||||||
if not name in CLASS_CACHE:
|
if not name in CLASS_CACHE:
|
||||||
profile_data = resolve_profile_data(name)
|
profile_data = PROFILES[name]
|
||||||
class_name = '%sProfile' % clean(name)
|
profile_name = clean(name)
|
||||||
new_class = type(class_name, (Profile,), {'profile_data': profile_data})
|
class_name = '{}{}Profile'.format(
|
||||||
|
profile_name[0].upper(), profile_name[1:])
|
||||||
|
new_class = type(class_name, (BaseProfile,), {'profile_data': profile_data})
|
||||||
CLASS_CACHE[name] = new_class
|
CLASS_CACHE[name] = new_class
|
||||||
|
|
||||||
return CLASS_CACHE[name]
|
return CLASS_CACHE[name]
|
||||||
|
@ -64,20 +81,20 @@ def clean(s):
|
||||||
return str(s)
|
return str(s)
|
||||||
|
|
||||||
|
|
||||||
def resolve_profile_data(name):
|
# For users, who want to provide their profile
|
||||||
data = PROFILES[name]
|
class Profile(get_profile_class('default')):
|
||||||
inherits = data.get('inherits')
|
|
||||||
if not inherits:
|
def __init__(self, columns=None):
|
||||||
return data
|
super(Profile, self).__init()
|
||||||
|
|
||||||
|
self.columns = columns
|
||||||
|
|
||||||
|
def get_columns(self, font):
|
||||||
|
if self.columns is not None:
|
||||||
|
return columns
|
||||||
|
|
||||||
|
return super(Profile, self).get_columns(font)
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,252 +0,0 @@
|
||||||
# Define code pages that implementors are likely not to find in iconv etc.
|
|
||||||
codepages:
|
|
||||||
blank: [
|
|
||||||
" ",
|
|
||||||
" ",
|
|
||||||
" ",
|
|
||||||
" ",
|
|
||||||
" ",
|
|
||||||
" ",
|
|
||||||
" ",
|
|
||||||
" ",
|
|
||||||
]
|
|
||||||
TCVN-3-1: [
|
|
||||||
" ",
|
|
||||||
" ",
|
|
||||||
" ăâêôơưđ ",
|
|
||||||
" àảãáạ ằẳẵắ ",
|
|
||||||
" ặầẩẫấậè ẻẽ",
|
|
||||||
"éẹềểễếệìỉ ĩíịò",
|
|
||||||
" ỏõóọồổỗốộờởỡớợù",
|
|
||||||
" ủũúụừửữứựỳỷỹýỵ ",
|
|
||||||
]
|
|
||||||
TCVN-3-2: [
|
|
||||||
" ",
|
|
||||||
" ",
|
|
||||||
" ĂÂ Ð ÊÔƠƯ ",
|
|
||||||
" ÀẢÃÁẠ ẰẲẴẮ ",
|
|
||||||
" ẶẦẨẪẤẬÈ ẺẼ",
|
|
||||||
"ÉẸỀỂỄẾỆÌỈ ĨÍỊÒ",
|
|
||||||
" ỎÕÓỌỒỔỖỐỘỜỞỠỚỢÙ",
|
|
||||||
" ỦŨÚỤỪỬỮỨỰỲỶỸÝỴ "
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
commands:
|
|
||||||
LineFeed:
|
|
||||||
name: Line feed
|
|
||||||
FeedAndCut:
|
|
||||||
name: Feed and cut
|
|
||||||
SetAbsolutePrintPos:
|
|
||||||
name: Set absolute print position
|
|
||||||
GraphicsData:
|
|
||||||
name: Graphics data
|
|
||||||
|
|
||||||
|
|
||||||
profiles:
|
|
||||||
default:
|
|
||||||
name: Default profile
|
|
||||||
description: Many recent Epson-branded thermal receipt printers
|
|
||||||
commands:
|
|
||||||
BarcodeB: true
|
|
||||||
BitImage: true
|
|
||||||
GraphicsData: true
|
|
||||||
QrCode: true
|
|
||||||
features:
|
|
||||||
starCommands: false
|
|
||||||
font:
|
|
||||||
0:
|
|
||||||
columns: 40
|
|
||||||
1:
|
|
||||||
columns: 50
|
|
||||||
colors:
|
|
||||||
- black
|
|
||||||
# Commented-out slots are TODO (might just need uncomment, might
|
|
||||||
# need verification/research)
|
|
||||||
codepages:
|
|
||||||
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:
|
|
||||||
inherits: default
|
|
||||||
name: Simple profile
|
|
||||||
codePages:
|
|
||||||
0: CP437
|
|
||||||
commands:
|
|
||||||
graphicsData: false
|
|
||||||
|
|
||||||
|
|
||||||
star:
|
|
||||||
name: Star-branded printers
|
|
||||||
inherits: default
|
|
||||||
features:
|
|
||||||
starCommands: false
|
|
||||||
|
|
||||||
|
|
||||||
printers:
|
|
||||||
P-822D:
|
|
||||||
inherits: default
|
|
||||||
manufacturer: "Epson"
|
|
||||||
commands:
|
|
||||||
graphicsData: false
|
|
||||||
|
|
||||||
# http://support.epostraders.co.uk/support-files/documents/3/l7O-TM-T88II_TechnicalRefGuide.pdf
|
|
||||||
TM-T88II:
|
|
||||||
inherits: default
|
|
||||||
manufacturer: "Epson"
|
|
||||||
fonts:
|
|
||||||
a:
|
|
||||||
columns: 42
|
|
||||||
b:
|
|
||||||
columns: 56
|
|
||||||
codePages:
|
|
||||||
0: PC437
|
|
||||||
1: Katakana
|
|
||||||
2: PC850
|
|
||||||
3: PC860
|
|
||||||
4: PC863
|
|
||||||
5: PC865
|
|
||||||
19: PC858
|
|
||||||
255: blank
|
|
||||||
|
|
||||||
# http://support.epostraders.co.uk/support-files/documents/3/l7O-TM-T88II_TechnicalRefGuide.pdf
|
|
||||||
TM-T88III:
|
|
||||||
inherits: default
|
|
||||||
manufacturer: "Epson"
|
|
||||||
fonts:
|
|
||||||
a:
|
|
||||||
columns: 42
|
|
||||||
b:
|
|
||||||
columns: 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: default
|
|
||||||
manufacturer: "Epson"
|
|
||||||
fonts:
|
|
||||||
a:
|
|
||||||
columns: 48
|
|
||||||
b:
|
|
||||||
columns: 64
|
|
||||||
kanji:
|
|
||||||
columns: 24
|
|
||||||
|
|
||||||
TM-P80 (42 column emulation mode):
|
|
||||||
inherits: TM-P80
|
|
||||||
fonts:
|
|
||||||
a:
|
|
||||||
columns: 42
|
|
||||||
b:
|
|
||||||
columns: 60
|
|
||||||
kanji:
|
|
||||||
columns: 21
|
|
||||||
|
|
||||||
TM-P60II 2 (58mm):
|
|
||||||
inherits: default
|
|
||||||
manufacturer: "Epson"
|
|
||||||
media:
|
|
||||||
width:
|
|
||||||
mm: 58
|
|
||||||
fonts:
|
|
||||||
a: {columns: 35}
|
|
||||||
b: {columns: 42}
|
|
||||||
c: {columns: 52}
|
|
||||||
|
|
||||||
TM-P60II 2 (60mm):
|
|
||||||
inherits: default
|
|
||||||
manufacturer: "Epson"
|
|
||||||
media:
|
|
||||||
width:
|
|
||||||
mm: 60
|
|
||||||
fonts:
|
|
||||||
a: {columns: 36}
|
|
||||||
b: {columns: 43}
|
|
||||||
c: {columns: 54}
|
|
||||||
|
|
||||||
TM-P20 2:
|
|
||||||
inherits: default
|
|
||||||
manufacturer: "Epson"
|
|
||||||
fonts: [a, b c, d, e, f]
|
|
||||||
|
|
||||||
TM-T90:
|
|
||||||
inherits: default
|
|
||||||
manufacturer: "Epson"
|
|
||||||
colors:
|
|
||||||
- black
|
|
||||||
- red
|
|
|
@ -93,14 +93,17 @@ TXT_UNDERL_ON = ESC + b'\x2d\x01' # Underline font 1-dot ON
|
||||||
TXT_UNDERL2_ON = ESC + b'\x2d\x02' # Underline font 2-dot ON
|
TXT_UNDERL2_ON = ESC + b'\x2d\x02' # Underline font 2-dot ON
|
||||||
TXT_BOLD_OFF = ESC + b'\x45\x00' # Bold font OFF
|
TXT_BOLD_OFF = ESC + b'\x45\x00' # Bold font OFF
|
||||||
TXT_BOLD_ON = ESC + b'\x45\x01' # Bold font ON
|
TXT_BOLD_ON = ESC + b'\x45\x01' # Bold font ON
|
||||||
TXT_FONT_A = ESC + b'\x4d\x00' # Font type A
|
|
||||||
TXT_FONT_B = ESC + b'\x4d\x01' # Font type B
|
|
||||||
TXT_ALIGN_LT = ESC + b'\x61\x00' # Left justification
|
TXT_ALIGN_LT = ESC + b'\x61\x00' # Left justification
|
||||||
TXT_ALIGN_CT = ESC + b'\x61\x01' # Centering
|
TXT_ALIGN_CT = ESC + b'\x61\x01' # Centering
|
||||||
TXT_ALIGN_RT = ESC + b'\x61\x02' # Right justification
|
TXT_ALIGN_RT = ESC + b'\x61\x02' # Right justification
|
||||||
TXT_INVERT_ON = GS + b'\x42\x01' # Inverse Printing ON
|
TXT_INVERT_ON = GS + b'\x42\x01' # Inverse Printing ON
|
||||||
TXT_INVERT_OFF = GS + b'\x42\x00' # Inverse Printing OFF
|
TXT_INVERT_OFF = GS + b'\x42\x00' # Inverse Printing OFF
|
||||||
|
|
||||||
|
# Fonts
|
||||||
|
SET_FONT = lambda n: ESC + b'\x4d' + n
|
||||||
|
TXT_FONT_A = SET_FONT(b'\x00') # Font type A
|
||||||
|
TXT_FONT_B = SET_FONT(b'\x01') # Font type B
|
||||||
|
|
||||||
# Char code table
|
# Char code table
|
||||||
CHARCODE_PC437 = ESC + b'\x74\x00' # USA: Standard Europe
|
CHARCODE_PC437 = ESC + b'\x74\x00' # USA: Standard Europe
|
||||||
CHARCODE_JIS = ESC + b'\x74\x01' # Japanese Katakana
|
CHARCODE_JIS = ESC + b'\x74\x01' # Japanese Katakana
|
||||||
|
|
|
@ -452,8 +452,8 @@ class Escpos(object):
|
||||||
col_count = self.profile.get_columns(font) if columns is None else columns
|
col_count = self.profile.get_columns(font) if columns is None else columns
|
||||||
self.text(textwrap.fill(txt, col_count))
|
self.text(textwrap.fill(txt, col_count))
|
||||||
|
|
||||||
def set(self, align='left', font='a', text_type='normal', width=1, height=1, density=9, invert=False, smooth=False,
|
def set(self, align='left', font='a', text_type='normal', width=1,
|
||||||
flip=False):
|
height=1, density=9, invert=False, smooth=False, flip=False):
|
||||||
""" Set text properties by sending them to the printer
|
""" Set text properties by sending them to the printer
|
||||||
|
|
||||||
:param align: horizontal position for text, possible values are:
|
:param align: horizontal position for text, possible values are:
|
||||||
|
@ -463,7 +463,9 @@ class Escpos(object):
|
||||||
* RIGHT
|
* RIGHT
|
||||||
|
|
||||||
*default*: LEFT
|
*default*: LEFT
|
||||||
:param font: font type, possible values are A or B, *default*: A
|
|
||||||
|
:param font: font given as an index, a name, or one of the
|
||||||
|
special values 'a' or 'b', refering to fonts 0 and 1.
|
||||||
:param text_type: text type, possible values are:
|
:param text_type: text type, possible values are:
|
||||||
|
|
||||||
* B for bold
|
* B for bold
|
||||||
|
@ -528,10 +530,8 @@ class Escpos(object):
|
||||||
self._raw(TXT_BOLD_OFF)
|
self._raw(TXT_BOLD_OFF)
|
||||||
self._raw(TXT_UNDERL_OFF)
|
self._raw(TXT_UNDERL_OFF)
|
||||||
# Font
|
# Font
|
||||||
if font.upper() == "B":
|
self._raw(SET_FONT(six.int2byte(self.profile.get_font(font))))
|
||||||
self._raw(TXT_FONT_B)
|
|
||||||
else: # DEFAULT FONT: A
|
|
||||||
self._raw(TXT_FONT_A)
|
|
||||||
# Align
|
# Align
|
||||||
if align.upper() == "CENTER":
|
if align.upper() == "CENTER":
|
||||||
self._raw(TXT_ALIGN_CT)
|
self._raw(TXT_ALIGN_CT)
|
||||||
|
|
Loading…
Reference in New Issue