python-escpos/src/escpos/capabilities.py

115 lines
2.9 KiB
Python
Raw Normal View History

import re
2016-08-30 10:26:09 +00:00
import six
from os import path
import yaml
2016-08-30 10:26:09 +00:00
# Load external printer database
with open(path.join(path.dirname(__file__), 'capabilities.json')) as f:
CAPABILITIES = yaml.load(f)
2016-08-30 10:26:09 +00:00
PROFILES = CAPABILITIES['profiles']
2016-08-30 10:26:09 +00:00
class NotSupported(Exception):
"""Raised if a requested feature is not suppored by the
printer profile.
"""
2016-08-30 10:26:09 +00:00
pass
BARCODE_B = 'barcodeB'
2016-08-30 10:26:09 +00:00
class BaseProfile(object):
"""This respresents a printer profile.
A printer profile knows about the number of columns, supported
features, colors and more.
"""
profile_data = {}
def __getattr__(self, name):
return self.profile_data[name]
2016-08-30 10:26:09 +00:00
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(
'"{}" is not a valid font in the current profile'.format(font))
2016-08-30 10:26:09 +00:00
return font
def get_columns(self, font):
""" Return the number of columns for the given font.
"""
2016-08-30 10:26:09 +00:00
font = self.get_font(font)
return self.fonts[six.text_type(font)]['columns']
def supports(self, feature):
"""Return true/false for the given feature.
"""
return self.features.get(feature)
def get_code_pages(self):
"""Return the support code pages as a {name: index} dict.
"""
return {v: k for k, v in self.codePages.items()}
def get_profile(name=None, **kwargs):
2016-08-30 10:26:09 +00:00
"""Get the profile by name; if no name is given, return the
default profile.
"""
if isinstance(name, Profile):
return name
clazz = get_profile_class(name or 'default')
return clazz(**kwargs)
CLASS_CACHE = {}
def get_profile_class(name):
2016-08-30 10:26:09 +00:00
"""For the given profile name, load the data from the external
database, then generate dynamically a class.
"""
2017-01-29 23:50:58 +00:00
if name not in CLASS_CACHE:
2016-08-30 10:26:09 +00:00
profile_data = PROFILES[name]
profile_name = clean(name)
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
return CLASS_CACHE[name]
def clean(s):
2017-01-29 23:10:14 +00:00
# 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)
2016-08-30 10:26:09 +00:00
class Profile(get_profile_class('default')):
"""
For users, who want to provide their profile
"""
2017-01-29 23:10:14 +00:00
2016-08-30 14:13:38 +00:00
def __init__(self, columns=None, features=None):
super(Profile, self).__init__()
2016-08-30 10:26:09 +00:00
self.columns = columns
2016-08-30 14:13:38 +00:00
self.features = features or {}
2016-08-30 10:26:09 +00:00
def get_columns(self, font):
if self.columns is not None:
2016-08-30 11:27:48 +00:00
return self.columns
2016-08-30 10:26:09 +00:00
return super(Profile, self).get_columns(font)