Compare commits
12 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c9f62c923d | |||
|
|
09dc6c51af | ||
|
|
101b5cadb5 | ||
|
|
411ea91084 | ||
| c961541302 | |||
| 88ad1d3f6c | |||
| 4e2ffb0224 | |||
| 4092320c79 | |||
|
|
2e3d6bba32 | ||
|
|
1746db8e37 | ||
|
|
df9b93e2da | ||
|
|
d40a59af89 |
12 changed files with 189 additions and 19 deletions
55
README.md
Normal file
55
README.md
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
# Setup
|
||||
On a raspberry pi:
|
||||
```
|
||||
user: pi
|
||||
password: itoldyoualready
|
||||
```
|
||||
|
||||
## Install
|
||||
from the home directory:
|
||||
|
||||
`git clone https://code.techinc.nl/emilevs/label_printer.git`\
|
||||
`sudo apt install python3-flask`\
|
||||
`sudo apt install golang`
|
||||
|
||||
(there might be more dependencies, I wasn't starting from a clean raspbian install when testing this)
|
||||
|
||||
navigate into the label_printer directory
|
||||
|
||||
run `make` to build the go library
|
||||
|
||||
`cp label_printer.service to /etc/systemd/system/`
|
||||
|
||||
```
|
||||
sudo systemctl enable label_printer
|
||||
sudo systemctl start label_printer
|
||||
```
|
||||
|
||||
The interface will now be running on localhost:5000, which we need to route to the outside world in the next step
|
||||
|
||||
## Setup the nginx
|
||||
sudo apt install nginx
|
||||
edit /etx/nginx/sites-available/default and change the contents of:
|
||||
```
|
||||
location / { }
|
||||
```
|
||||
|
||||
to:
|
||||
|
||||
```
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:5000;
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
sudo systemctl restart nginx
|
||||
```
|
||||
|
||||
## Setup static ip on the pi
|
||||
edit `/etc/dhcpcd.conf` and add:
|
||||
```
|
||||
static ip_address=10.209.10.2/24
|
||||
static routers=10.209.10.254
|
||||
static domain_name_servers=10.209.10.255
|
||||
```
|
||||
61
app.py
61
app.py
|
|
@ -3,11 +3,12 @@ from flask import Flask, render_template, request, redirect, url_for, session, s
|
|||
from PIL import Image
|
||||
from gen_image import gen_image
|
||||
from config import *
|
||||
from datetime import datetime
|
||||
from datetime import *
|
||||
from print import *
|
||||
from process_image import *
|
||||
from file_handler import *
|
||||
from colorama import Fore, Style
|
||||
from fonts import fonts
|
||||
import os
|
||||
|
||||
text_image_filename = "text_image.png"
|
||||
|
|
@ -44,12 +45,25 @@ templates = {
|
|||
"pos": 130
|
||||
},
|
||||
"cut": True
|
||||
},
|
||||
"Remove by": {
|
||||
"text1": {
|
||||
"string": "Remove by",
|
||||
"size": 130,
|
||||
"pos": 0
|
||||
},
|
||||
"text2": {
|
||||
"string": "",
|
||||
"size": 50,
|
||||
"pos": 130
|
||||
},
|
||||
"cut": True
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def render_text_template(info=None, info_color=None):
|
||||
return render_template('text.html', filename=session["text image path"], text1=session["text1"], text2=session["text2"], cut=session["cut"], info=info, info_color=info_color)
|
||||
def render_text_template(info=None, info_color=None, scrollDown=None):
|
||||
return render_template('text.html', filename=session["text image path"], text1=session["text1"], text2=session["text2"], cut=session["cut"], info=info, info_color=info_color, scrollDown=scrollDown, fonts=session["fonts"])
|
||||
|
||||
|
||||
|
||||
|
|
@ -60,8 +74,7 @@ def render_image_template(info=None, info_color=None):
|
|||
@app.route('/', methods=['GET', 'POST'])
|
||||
def base():
|
||||
check_for_new_user(session)
|
||||
return render_template('base.html')
|
||||
|
||||
return render_template('default.html')
|
||||
|
||||
# gets triggered when the user_data folder for the user is created
|
||||
@new_user_handler
|
||||
|
|
@ -74,6 +87,7 @@ def on_new_user(session):
|
|||
session["text1"] = templates["DNH"]["text1"]
|
||||
session["text2"] = templates["DNH"]["text2"]
|
||||
session["cut"] = templates["DNH"]["cut"]
|
||||
session["fonts"] = fonts()
|
||||
|
||||
|
||||
|
||||
|
|
@ -83,11 +97,12 @@ def image():
|
|||
|
||||
if request.method == 'POST':
|
||||
if 'image' not in request.files:
|
||||
return redirect(request.url)
|
||||
return render_image_template()
|
||||
|
||||
file = request.files['image']
|
||||
|
||||
if file.filename == '':
|
||||
return redirect(request.url)
|
||||
return render_image_template()
|
||||
if file:
|
||||
extension = os.path.splitext(file.filename)[1]
|
||||
|
||||
|
|
@ -140,9 +155,13 @@ def text_template():
|
|||
|
||||
if request.method == 'POST':
|
||||
template = templates[request.form["template"]]
|
||||
|
||||
if request.form["template"] == "Food":
|
||||
template["text2"]["string"] = datetime.now().strftime('%Y-%m-%d')
|
||||
|
||||
if request.form["template"] == "Remove by":
|
||||
template["text2"]["string"] = (datetime.now() + timedelta(weeks=3)).strftime('%Y-%m-%d')
|
||||
|
||||
session["text1"] = template["text1"]
|
||||
session["text2"] = template["text2"]
|
||||
session["cut"] = template["cut"]
|
||||
|
|
@ -166,7 +185,30 @@ def text_form():
|
|||
"pos": int(request.form["pos2"]),
|
||||
}
|
||||
|
||||
message, status, img = gen_image(label_width, session["text1"], session["text2"])
|
||||
# Clear previously saved font
|
||||
for font in session["fonts"]:
|
||||
session["fonts"][font]["selected"] = False
|
||||
|
||||
# If an invalid font has been submitted, just default to the first one
|
||||
chosen_font = request.form["font"]
|
||||
if chosen_font in session["fonts"]:
|
||||
session["fonts"][chosen_font]["selected"] = True
|
||||
else:
|
||||
first_font = next(iter(session["fonts"]))
|
||||
session["fonts"][first_font]["selected"] = True
|
||||
|
||||
# Select the first font that has been marked as selected
|
||||
chosen_font = None
|
||||
for font in session["fonts"]:
|
||||
f = session["fonts"][font]
|
||||
if f["selected"]:
|
||||
chosen_font = f
|
||||
|
||||
# If the font is still None, something has gone wrong
|
||||
if chosen_font is None:
|
||||
return
|
||||
|
||||
message, status, img = gen_image(label_width, session["text1"], session["text2"], chosen_font)
|
||||
|
||||
if status == "Error":
|
||||
session["text image path"] = None
|
||||
|
|
@ -176,10 +218,11 @@ def text_form():
|
|||
|
||||
session["text image path"] = text_image_filename
|
||||
img.save(get_file_path(session, session["text image path"]))
|
||||
|
||||
print()
|
||||
print(session["text image path"])
|
||||
|
||||
return render_text_template()
|
||||
return render_text_template(scrollDown = True)
|
||||
|
||||
|
||||
@app.route('/text', methods=['GET', 'POST'])
|
||||
|
|
|
|||
21
fonts.py
Normal file
21
fonts.py
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
class Font:
|
||||
def __init__(self, name: str, path: str, stroke_width: int, default: bool = False) -> None:
|
||||
self.name = name
|
||||
self.path = path
|
||||
self.stroke_width = stroke_width
|
||||
self.selected = default
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
"name": self.name,
|
||||
"path": self.path,
|
||||
"selected": self.selected,
|
||||
"stroke_width": self.stroke_width
|
||||
}
|
||||
|
||||
def fonts():
|
||||
return {
|
||||
"CYBER": Font("CYBER", "resources/OCRAEXT.TTF", 3, True).to_dict(),
|
||||
"ComicMono": Font("Comic Sans Mono", "resources/ComicMono.ttf", 0).to_dict(),
|
||||
"EstupidoEspezial": Font("¡¡¡Estupido-Espezial!!!", "resources/Estupido Espezial.ttf", 1).to_dict()
|
||||
}
|
||||
10
gen_image.py
10
gen_image.py
|
|
@ -3,11 +3,11 @@ from PIL import Image, ImageDraw, ImageFont
|
|||
from colorama import Fore, Style
|
||||
from config import *
|
||||
|
||||
font = "resources/ComicMono.ttf"
|
||||
#font = "resources/ComicMono.ttf"
|
||||
|
||||
def gen_image(height, text1, text2):
|
||||
font1 = ImageFont.truetype(font, size=text1["size"])
|
||||
font2 = ImageFont.truetype(font, size=text2["size"])
|
||||
def gen_image(height, text1, text2, font):
|
||||
font1 = ImageFont.truetype(font["path"], size=text1["size"])
|
||||
font2 = ImageFont.truetype(font["path"], size=text2["size"])
|
||||
|
||||
text1["string"] = text1["string"].replace("\r\n", "\n")
|
||||
text2["string"] = text2["string"].replace("\r\n", "\n")
|
||||
|
|
@ -33,7 +33,7 @@ def gen_image(height, text1, text2):
|
|||
|
||||
draw = ImageDraw.Draw(img)
|
||||
|
||||
draw.text((0, text1["pos"]), text1["string"], font=font1)
|
||||
draw.text((0, text1["pos"]), text1["string"], font=font1, stroke_width=font["stroke_width"], stroke_fill="black")
|
||||
draw.text((0, text2["pos"]), text2["string"], font=font2)
|
||||
|
||||
|
||||
|
|
|
|||
9
requirements.txt
Normal file
9
requirements.txt
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
blinker==1.9.0
|
||||
click==8.1.7
|
||||
colorama==0.4.6
|
||||
Flask==3.1.0
|
||||
itsdangerous==2.2.0
|
||||
Jinja2==3.1.4
|
||||
MarkupSafe==3.0.2
|
||||
pillow==11.0.0
|
||||
Werkzeug==3.1.3
|
||||
BIN
resources/Estupido Espezial.ttf
Normal file
BIN
resources/Estupido Espezial.ttf
Normal file
Binary file not shown.
BIN
resources/OCRAEXT.TTF
Normal file
BIN
resources/OCRAEXT.TTF
Normal file
Binary file not shown.
|
|
@ -4,7 +4,6 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Image Upload</title>
|
||||
|
||||
<style>
|
||||
body {
|
||||
|
|
@ -82,6 +81,21 @@
|
|||
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
{% if scrollDown %}
|
||||
localStorage.setItem('scrollpos', 99999);
|
||||
{% endif %}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
var scrollpos = localStorage.getItem('scrollpos');
|
||||
if (scrollpos) window.scrollTo(0, scrollpos);
|
||||
});
|
||||
|
||||
window.onbeforeunload = function(e) {
|
||||
localStorage.setItem('scrollpos', window.scrollY);
|
||||
};
|
||||
</script>
|
||||
|
||||
<div class="container">
|
||||
<a href="text" class="box">
|
||||
<div>Text</div>
|
||||
|
|
|
|||
10
templates/default.html
Normal file
10
templates/default.html
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<!-- SPDX-License-Identifier: GPL-3.0-or-later -->
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block head %}
|
||||
<title>Labelprinter</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
|
||||
|
|
@ -1,12 +1,16 @@
|
|||
<!-- SPDX-License-Identifier: GPL-3.0-or-later -->
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block head %}
|
||||
<title>LabelPrinter Image upload</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Upload Image</h1>
|
||||
<form method="POST" action="/image" enctype="multipart/form-data">
|
||||
<input type="file" name="image">
|
||||
<input type="submit" value="Generate preview"><br>
|
||||
Recommended image height = {{label_width}}, taller images will be scaled
|
||||
Recommended image height = {{label_width}}, other sizes will be scaled
|
||||
</form>
|
||||
|
||||
{% if info %}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
<!-- SPDX-License-Identifier: GPL-3.0-or-later -->
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block head %}
|
||||
<title>LabelPrinter Label generator</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<form method="POST" action="/text-template">
|
||||
|
|
@ -8,13 +11,14 @@
|
|||
<select name="template">
|
||||
<option value="DNH">Do not hack</option>
|
||||
<option value="Food">Food</option>
|
||||
<option value="Remove by">Remove by</option>
|
||||
</select>
|
||||
<input type="submit" value="Apply">
|
||||
</form>
|
||||
|
||||
|
||||
<form method="POST" action="/text-form">
|
||||
<h2>String 1</h2>
|
||||
<h2>Top text</h2>
|
||||
Text:<br>
|
||||
<textarea name="string1">{{text1["string"]}}</textarea><br>
|
||||
Size: <input type="number" value="{{text1["size"]}}" name="size1" min="0" required
|
||||
|
|
@ -22,13 +26,23 @@
|
|||
Y position: <input type="number" value="{{text1["pos"]}}" name="pos1" required
|
||||
style="width: 40px"><br>
|
||||
|
||||
<h2>String 2</h2>
|
||||
<h2>Bottom text</h2>
|
||||
Text:<br>
|
||||
<textarea name="string2">{{text2["string"]}}</textarea><br>
|
||||
Size: <input type="number" value="{{text2["size"]}}" name="size2" min="0" required
|
||||
style="width: 40px"><br>
|
||||
Y position: <input type="number" value="{{text2["pos"]}}" name="pos2" required
|
||||
style="width: 40px"><br>
|
||||
style="width: 40px">
|
||||
|
||||
<h2>Font</h2>
|
||||
<label for="font">Font:</label>
|
||||
<select name="font">
|
||||
{% for font in fonts %}
|
||||
<option value="{{ font }}"{% if fonts[font].selected %} selected{% endif %}>{{ fonts[font].name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
|
||||
<br />
|
||||
<input type="submit" value="Generate preview">
|
||||
</form>
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue