1
0
Fork 0

Compare commits

...

6 Commits

Author SHA1 Message Date
vibe 27fa0f326f Update footer credits in template 2023-11-12 22:56:59 +01:00
vibe 7f2cfd5b31 Change file open mode to 'wb'
FFI used in the project uses bytes instead of str for writing purposes.
2023-11-12 22:56:21 +01:00
vibe 92a1286055 Fix healthcheck in python3
Subprocess returns stdout as bytes instead of str nowadays, so I
addressed it in a quick and dirty way.

The extra return in healthcheck function is added there to stop the app
from complaining about it and also because I discovered that the
solution for tracking printer status here is essentially hardcoded for
English, while my local test environment outputs lpstat logs
in Ukrainian.
2023-11-12 21:44:59 +01:00
vibe db76e8a64f WIP: Upgrade to Python3, change dependencies
Due to old pangocairo bindings seemingly not being updated anymore
I resorted to using cffi-based bindings for now.
Current problems with pango and pangocairo bindings is that they are
incomplete, and it seems like outside HTML parsing there are some broken
things about font rendering, which I need to deal with. I might look
into rebuilding this functionality using something else, maybe Pillow.

Health check was disabled for now so I can focus on image rendering.
2023-11-07 03:14:38 +01:00
vibe b366bb265b Initialize poetry in the project 2023-11-07 01:47:57 +01:00
vibe f8a294058f Rewrite README.md to be more descriptive 2023-11-06 02:01:44 +01:00
4 changed files with 50 additions and 27 deletions

View File

@ -1 +1,3 @@
Use the source, luke :^)
# Hackerspace Label Printing Interface
Web interface for accessing label printers on the premises of Warsaw Hackerspace. Currently only supports DYMO LabelWriter.

18
pyproject.toml Normal file
View File

@ -0,0 +1,18 @@
[tool.poetry]
name = "labelmaker"
version = "0.1.0"
description = "Web interface for accessing label printers available at hswaw"
authors = ["Your Name <you@example.com>"]
readme = "README.md"
[tool.poetry.dependencies]
python = "^3.12"
flask = "^3.0.0"
cairocffi = "^1.6.1"
pangocffi = "^0.12.0"
pangocairocffi = "^0.7.0"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

View File

@ -1,15 +1,15 @@
import json
import math
import os
import StringIO
import io
import subprocess
import tempfile
import time
import cairo
import cairocffi as cairo
import flask
import pango
import pangocairo
from flask import json
import pangocffi as pango
import pangocairocffi as pangocairo
class App(flask.Flask):
def __init__(self, *args, **kwargs):
@ -50,26 +50,26 @@ class Renderer(object):
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)
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.set_markup('<span font_desc="%s">%s</span>' % (fontname, text))
layout.apply_markup('<span font_desc="%s">%s</span>' % (fontname, text))
else:
font = pango.FontDescription(fontname)
layout.set_font_description(font)
layout.set_text(text)
font = pango.FontDescription()
font.family = fontname
layout.font_description = font
layout.text = text
if y == -1:
self.context.translate(0, (self.height - layout.get_size()[1]/pango.SCALE)/2)
self.context.translate(0, (self.height - pango.units_to_double(layout.get_size()[1]))/2)
self.context.set_source_rgb(0, 0, 0)
pangocairo_context.update_layout(layout)
pangocairo_context.show_layout(layout)
pangocairo.update_layout(self.context, layout)
pangocairo.show_layout(self.context, layout)
self.context.restore()
@ -81,10 +81,10 @@ 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'])
output = subprocess.run(['lpstat', '-p', '-d'], capture_output=True).stdout
mark = False
for line in output.split('\n'):
line = line.strip()
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'
@ -98,6 +98,8 @@ def healthcheck():
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():
@ -111,7 +113,7 @@ def stuff_preview(size):
r = Renderer()
r.render_text(text, 'Sans {}'.format(size), 0, -1, html)
sio = StringIO.StringIO()
sio = io.BytesIO()
r.surface.write_to_png(sio)
sio.seek(0)
return flask.send_file(sio, mimetype='image/png')
@ -122,7 +124,7 @@ def stuff_print(size):
if not healthcheck()[0]:
return 'Printer is down.'
last = app.last
print last, time.time() - 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')
@ -130,7 +132,7 @@ def stuff_print(size):
r = Renderer()
r.render_text(text, 'Sans {}'.format(size), 0, -1, html)
path = '/tmp/hslabel'
f = open(path, 'w')
f = open(path, 'wb')
r.surface.write_to_png(f)
f.flush()

View File

@ -90,8 +90,9 @@ $ curl -d "" http://label.waw.hackerspace.pl/stuff/print/60/?text=foobar&amp;htm
</div>
<footer class="footer">
<div class="container">
<p class="text-muted" style="margin: 20px 0;">Cobbled together by <a href="https://q3k.org/">q3k</a>. Sauce
on <a href="https://code.hackerspace.pl/q3k/labelmaker">code.hackerspace.pl/q3k/labelmaker</a></p>
<p class="text-muted" style="margin: 20px 0;">Cobbled together by <a href="https://q3k.org/">q3k</a>,
updated to Python 3 by vibe. Sauce
on <a href="https://code.hackerspace.pl/hswaw/labelmaker">code.hackerspace.pl/hswaw/labelmaker</a></p>
</div>
</footer>
<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>