Match the current printer-db format.

This commit is contained in:
Michael Elsdörfer 2016-08-30 12:26:09 +02:00
parent 216184f43f
commit a07f84a5bc
6 changed files with 67 additions and 297 deletions

View File

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

View File

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

View File

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

View File

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

View File

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