1
0
Fork 0
labelmaker/render.py

150 lines
4.3 KiB
Python

import math
import os
import io
import subprocess
import time
import cairocffi as cairo
import flask
from flask import json
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)
# fill it white, while we're at it
context.rectangle(0, 0, width, height)
context.set_source_rgb(1, 1, 1)
context.fill()
context.translate(width, 0)
context.rotate(math.pi/2)
# yolo
self.width, self.height = height, width
self.context = context
self.surface = surface
def export_png(self, name):
with open(name, 'w') as f:
self.surface.write_to_png(f)
def render_text(self, text, fontname, x, y, html=False):
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.width = pango.units_from_double(self.width)
layout.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:
font = pango.FontDescription()
font.family = fontname
layout.font_description = 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.run(['lpstat', '-p', '-d'], capture_output=True).stdout
mark = False
for line in output.split(b'\n'):
line = line.strip().decode('utf-8')
if line.startswith('printer DYMO_LabelWriter_450'):
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 is down or there\'s something wrong with lpstat output.'
@app.route('/health')
def health():
ok, details = healthcheck()
return json.dumps({'ok': ok, 'details': details})
@app.route('/stuff/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)
sio = io.BytesIO()
r.surface.write_to_png(sio)
sio.seek(0)
return flask.send_file(sio, mimetype='image/png')
DELAY = 5
@app.route('/stuff/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 -P DYMO_LabelWriter_450 {}'.format(path)
os.system(ex)
f.close()
app.last = time.time()
return 'ok'
if __name__ == '__main__':
app.run(debug=True)