labelmaker/labelmaker/__main__.py

146 lines
4.4 KiB
Python

import json
import math
import os
import subprocess
import time
import cairocffi as cairo
import flask
import pangocffi as pango
import pangocairocffi as pangocairo
class App(flask.Flask):
def __init__(self, *args, **kwargs):
super(App, self).__init__(*args, **kwargs)
self.last = 0
self.health = (0, False, "unknown")
app = App(__name__)
class Renderer(object):
INCH_PER_MM = 0.039
DPI = 300
def __init__(self, size=(36, 89)):
width, height = [int(s * self.INCH_PER_MM * self.DPI) for s in size]
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
context = cairo.Context(surface)
context.rectangle(0, 0, width, height)
context.set_source_rgb(1, 1, 1)
context.fill()
context.translate(width, 0)
context.rotate(math.pi/2)
self.width, self.height = height, width
self.font = pango.FontDescription()
self.context = context
self.surface = surface
def export_png(self, name):
with open(name, 'wb') as f:
self.surface.write_to_png(f)
def render_text(self, text, fontname, x, y, html=False):
print("Fontname:", fontname)
self.context.save()
if y != -1:
self.context.translate(x, y)
self.context.set_antialias(cairo.ANTIALIAS_SUBPIXEL)
layout = pangocairo.create_layout(self.context)
layout._set_width(pango.units_from_double(self.width))
layout._set_alignment(pango.Alignment.CENTER)
if html:
# Absolutely horrifying hack to fix broken text wrapping
layout.apply_markup('<span font_desc="%s">%s</span>' % (fontname, text))
else:
self.font.family = fontname.split()[0]
self.font.size = pango.units_from_double(int(fontname.split()[1]))
layout.font_description = self.font
layout.text = text
if y == -1:
self.context.translate(0, (self.height - pango.units_to_double(layout.get_size()[1]))/2)
self.context.set_source_rgb(0, 0, 0)
pangocairo.update_layout(self.context, layout)
pangocairo.show_layout(self.context, layout)
self.context.restore()
@app.route('/')
def index():
return flask.render_template('index.html')
def healthcheck():
last_checked, last_status, last_details = app.health
if time.time() - last_checked < 1:
return last_status, last_details
output = subprocess.check_output(['lpstat', '-p', '-d']).decode()
mark = False
for line in output.split('\n'):
line = line.strip()
if line.startswith('printer DYMO_LabelWriter450'):
if 'is idle.' in line:
return True, 'Idle'
mark = True
continue
if mark:
if line.startswith('Ready to print'):
app.health = (time.time(), True, line)
return True, line
else:
app.health = (time.time(), False, line)
return False, line
mark = False
return False, "PRINTER_NOT_CONNECTED"
@app.route('/health')
def health():
ok, details = healthcheck()
return json.dumps({'ok': ok, 'details': details})
@app.route('/api/preview/<int:size>/')
def stuff_preview(size):
text = flask.request.args.get('text')
html = flask.request.args.get('html') == '1'
r = Renderer()
r.render_text(text, 'Sans {}'.format(size), 0, -1, html)
preview = r.surface.write_to_png()
return flask.Response(preview, mimetype='image/png')
DELAY = 5
@app.route('/api/print/<int:size>/', methods=['POST'])
def stuff_print(size):
if not healthcheck()[0]:
return 'Printer is down.'
last = app.last
print(last, time.time() - last)
if time.time() - last < DELAY:
return 'Please wait {} more seconds before next print.'.format(int(DELAY - (time.time() - last)))
text = flask.request.args.get('text')
html = flask.request.args.get('html') == '1'
r = Renderer()
r.render_text(text, 'Sans {}'.format(size), 0, -1, html)
path = '/tmp/hslabel'
f = open(path, 'wb')
r.surface.write_to_png(f)
f.flush()
f.close()
time.sleep(1)
ex = 'lpr -o PageSize=w118h252 -P DYMO_LabelWriter450 {}'.format(path)
os.system(ex)
f.close()
app.last = time.time()
return 'ok'
def main():
app.run(host="0.0.0.0", port=5000, debug=True)
if __name__ == '__main__':
main()