brother_ql_web/brother_ql_web.py

194 lines
6.0 KiB
Python
Raw Normal View History

2016-11-20 15:57:39 +02:00
#!/usr/bin/env python
"""
2016-11-20 16:10:07 +02:00
This is a web service to print labels on Brother QL label printers.
2016-11-20 15:57:39 +02:00
"""
import sys, logging, socket, os, subprocess, functools
from bottle import run, route, response, request
from PIL import Image, ImageDraw, ImageFont
import numpy as np
import markdown
from brother_ql.devicedependent import models
from brother_ql import BrotherQLRaster, create_label
2016-11-20 15:57:39 +02:00
from brother_ql.backends import backend_factory, guess_backend
MODEL = None
BACKEND_CLASS = None
BACKEND_STRING_DESCR = None
logger = logging.getLogger(__name__)
# globals:
DEBUG = False
FONTS = None
DEFAULT_FONT = None
DEFAULT_FONTS = [
{'family': 'Minion Pro', 'style': 'Semibold'},
{'family': 'Linux Libertine', 'style': 'Regular'},
{'family': 'DejaVu Serif', 'style': 'Book'},
]
@route('/')
def index():
2016-11-20 16:10:07 +02:00
INDEX_MD = __doc__ + """
Go to [/api/print/text/Your_Text](/api/print/text/)
to print a label (replace `Your_Text` with your text).
"""
return markdown.markdown(INDEX_MD)
2016-11-20 15:57:39 +02:00
def get_fonts(folder=None):
"""
Scan a folder (or the system) for .ttf / .otf fonts and
return a dictionary of the structure family -> style -> file path
"""
fonts = {}
if folder:
cmd = ['fc-scan', '--format', '"%{file}:%{family}:style=%{style}\n"', folder]
else:
cmd = ['fc-list', ':', 'file', 'family', 'style']
for line in subprocess.check_output(cmd).decode('utf-8').split("\n"):
logger.debug(line)
line.strip()
if not line: continue
if 'otf' not in line and 'ttf' not in line: continue
parts = line.split(':')
path = parts[0]
families = parts[1].strip().split(',')
styles = parts[2].split('=')[1].split(',')
if len(families) == 1 and len(styles) > 1:
families = [families[0]] * len(styles)
elif len(families) > 1 and len(styles) == 1:
styles = [styles[0]] * len(families)
if len(families) != len(styles):
if DEBUG:
print("Problem with this font:")
print(line)
continue
for i in range(len(families)):
try: fonts[families[i]]
except: fonts[families[i]] = dict()
fonts[families[i]][styles[i]] = path
if DEBUG:
print("Added this font:")
print(families[i], styles[i], path)
return fonts
@route('/api/print/text')
@route('/api/print/text/')
@route('/api/print/text/<content>')
def print_text(content=None):
"""
API to print a label
returns: JSON
Ideas for additional URL parameters:
- alignment
"""
return_dict = {'success': False}
if content is None:
return_dict['error'] = 'Please provide the text for the label'
return return_dict
threshold = 170
fontsize = int(request.query.get('font_size', 100))
label_size = "62"
2016-11-20 15:57:39 +02:00
width = 720
margin = 0
height = 100 + 2*margin
try:
font_family = request.query.get('font_family')
font_style = request.query.get('font_style')
if font_family is None:
font_family = DEFAULT_FONT['family']
font_style = DEFAULT_FONT['style']
if font_style is None:
font_style = 'Regular'
font_path = FONTS[font_family][font_style]
except KeyError:
return_dict['error'] = "Couln't find the font & style"
return return_dict
im = Image.new('L', (width, height), 'white')
draw = ImageDraw.Draw(im)
im_font = ImageFont.truetype(font_path, fontsize)
textsize = draw.textsize(content, font=im_font)
vertical_offset = (height - textsize[1])//2
horizontal_offset = max((width - textsize[0])//2, 0)
if 'ttf' in font_path: vertical_offset -= 10
offset = horizontal_offset, vertical_offset
if DEBUG: print("Offset: {}".format(offset))
draw.text(offset, content, (0), font=im_font)
if DEBUG: im.save('sample-out.png')
qlr = BrotherQLRaster(MODEL)
create_label(qlr, im, label_size, threshold=threshold, cut=True)
2016-11-20 15:57:39 +02:00
if not DEBUG:
try:
be = BACKEND_CLASS(BACKEND_STRING_DESCR)
be.write(qlr.data)
be.dispose()
del be
except Exception as e:
return_dict['message'] = str(e)
logger.warning('Exception happened: %s', e)
response.status = 500
return return_dict
return_dict['success'] = True
if DEBUG: return_dict['data'] = str(qlr.data)
return return_dict
def main():
global DEBUG, FONTS, DEFAULT_FONT, MODEL, BACKEND_CLASS, BACKEND_STRING_DESCR
import argparse
2016-11-20 16:10:07 +02:00
parser = argparse.ArgumentParser(description=__doc__)
2016-11-20 15:57:39 +02:00
parser.add_argument('--port', default=8013)
parser.add_argument('--loglevel', type=lambda x: getattr(logging, x.upper()), default='WARNING')
parser.add_argument('--font-folder', help='folder for additional .ttf/.otf fonts')
parser.add_argument('--model', default='QL-500', choices=models, help='The model of your printer (default: QL-500)')
parser.add_argument('printer', help='String descriptor for the printer to use (like tcp://192.168.0.23:9100 or file:///dev/usb/lp0)')
args = parser.parse_args()
DEBUG = args.loglevel == logging.DEBUG
logging.basicConfig(level=args.loglevel)
try:
selected_backend = guess_backend(args.printer)
except:
parser.error("Couln't guess the backend to use from the printer string descriptor")
BACKEND_CLASS = backend_factory(selected_backend)['backend_class']
BACKEND_STRING_DESCR = args.printer
MODEL = args.model
FONTS = get_fonts()
if args.font_folder:
FONTS.update(get_fonts(args.font_folder))
for font in DEFAULT_FONTS:
try:
FONTS[font['family']][font['style']]
DEFAULT_FONT = font
logger.debug("Selected the following default font: {}".format(font))
break
except: pass
if DEFAULT_FONT is None:
sys.stderr.write('Could not find any of the default fonts')
sys.exit()
run(host='', port=args.port, debug=args.loglevel==logging.DEBUG)
if __name__ == "__main__":
main()