import json import math import os import StringIO import subprocess import tempfile import time import cairo import flask import pango import 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) pangocairo_context = pangocairo.CairoContext(self.context) pangocairo_context.set_antialias(cairo.ANTIALIAS_SUBPIXEL) layout = pangocairo_context.create_layout() layout.set_width(self.width*pango.SCALE) layout.set_alignment(pango.ALIGN_CENTER) if html: # Absolutely horrifying hack to fix broken text wrapping layout.set_markup('%s' % (fontname, text)) else: font = pango.FontDescription(fontname) layout.set_font_description(font) layout.set_text(text) if y == -1: self.context.translate(0, (self.height - layout.get_size()[1]/pango.SCALE)/2) self.context.set_source_rgb(0, 0, 0) pangocairo_context.update_layout(layout) pangocairo_context.show_layout(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']) mark = False for line in output.split('\n'): line = line.strip() 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 @app.route('/health') def health(): ok, details = healthcheck() return json.dumps({'ok': ok, 'details': details}) @app.route('/stuff/preview//') 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 = StringIO.StringIO() r.surface.write_to_png(sio) sio.seek(0) return flask.send_file(sio, mimetype='image/png') DELAY = 5 @app.route('/stuff/print//', 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, 'w') 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)