forked from hswaw/labelmaker
Initial commit.
commit
9f17cf2b52
|
@ -0,0 +1,109 @@
|
|||
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
|
||||
|
||||
|
||||
app = App(__name__)
|
||||
|
||||
class Renderer(object):
|
||||
INCH_PER_MM = 0.039
|
||||
DPI = 300
|
||||
def __init__(self, size=(19,51)):
|
||||
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):
|
||||
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)
|
||||
font = pango.FontDescription(fontname)
|
||||
layout.set_font_description(font)
|
||||
layout.set_text(text)
|
||||
layout.set_alignment(pango.ALIGN_CENTER)
|
||||
|
||||
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')
|
||||
|
||||
@app.route('/stuff/preview/<int:size>/')
|
||||
def stuff_preview(size):
|
||||
text = flask.request.args.get('text')
|
||||
r = Renderer()
|
||||
r.render_text(text, 'Sans {}'.format(size), 0, -1)
|
||||
|
||||
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/<int:size>/', methods=['POST'])
|
||||
def stuff_print(size):
|
||||
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')
|
||||
r = Renderer()
|
||||
r.render_text(text, 'Sans {}'.format(size), 0, -1)
|
||||
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)
|
Binary file not shown.
|
@ -0,0 +1,97 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Hackerspace Printing System For Printing</title>
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title">Are you sure you want to print this?</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>These labels cost money. Don't be an asshole, and only print stuff that is really needed.</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Nevermind</button>
|
||||
<button type="button" id="btnconfirm" class="btn btn-primary">Yes please</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="page-header">
|
||||
<h1>Hackerspace Printing System For Printing Labels</h1>
|
||||
</div>
|
||||
<h3>Box 'o Stuff Label <small>For SAMLA boxes with common equipment</small></h3>
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
<h4>Preview</h4>
|
||||
<img id="preview" src="/stuff/preview/70/?text=dupa" style="border-width: 1px; border-style: solid; height: 300px;"/><br/>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<h4>Settings</h4>
|
||||
<div class="form-group">
|
||||
<label for="fontsize">Font Size</label>
|
||||
<input id="fontsize" value="60" style="width: 100px;" class="form-control" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="labeltext">Label Text</label>
|
||||
<input id="labeltext" value="Sample Text" style="width: 200px;" class="form-control" />
|
||||
</div>
|
||||
<button type="button" id="btnpreview" class="btn btn-default btn-lg"><span class="glyphicon glyphicon-picture" aria-hidden="true"></span> Preview</button>
|
||||
<button type="button" id="btnprint" class="btn btn-warning btn-lg"><span class="glyphicon glyphicon-print" aria-hidden="true"></span> Print!</button>
|
||||
</div>
|
||||
<div class="col-md-7">
|
||||
<h4>API</h4>
|
||||
<pre>$ # To get a label preview
|
||||
$ curl http://label.waw.hackerspace.pl/stuff/preview/60/?text=foobar | feh -
|
||||
$ # To print the label
|
||||
$ curl -d "" http://label.waw.hackerspace.pl/stuff/print/60/?text=foobar</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-growl/1.0.0/jquery.bootstrap-growl.min.js"></script>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
var generatePreview = function() {
|
||||
var fontsize = parseInt($("#fontsize").val());
|
||||
var text = encodeURIComponent($("#labeltext").val());
|
||||
$("#preview").attr("src", "/stuff/preview/" + fontsize + "/?text=" + text);
|
||||
};
|
||||
$("#btnpreview").click(generatePreview);
|
||||
var asked = false;
|
||||
var print = function() {
|
||||
var fontsize = parseInt($("#fontsize").val());
|
||||
var text = encodeURIComponent($("#labeltext").val());
|
||||
$.post("/stuff/print/" + fontsize + "/?text=" + text, function(data) {
|
||||
if (data == "ok") {
|
||||
$.bootstrapGrowl('Printed succesfully!', {'type': 'success'});
|
||||
} else {
|
||||
$.bootstrapGrowl('An error ocurred: ' + data, {'type': 'warning'});
|
||||
}
|
||||
});
|
||||
};
|
||||
$("#btnprint").click(function() {
|
||||
if (!asked) {
|
||||
$("#myModal").modal('show');
|
||||
} else {
|
||||
print();
|
||||
}
|
||||
});
|
||||
$("#btnconfirm").click(function() {
|
||||
asked = true;
|
||||
$("#myModal").modal('hide');
|
||||
print();
|
||||
});
|
||||
generatePreview();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue