Compare commits
10 commits
30d1cc8e3d
...
864dcb8160
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
864dcb8160 | ||
|
|
cd4df840b6 | ||
|
|
c4436d040b | ||
|
|
117efa3589 | ||
|
|
80bf0cf4e8 | ||
|
|
1d0f5cf199 | ||
|
|
78456105ac | ||
|
|
10d218bfba | ||
|
|
bd1c7ed477 | ||
|
|
29e254434f |
49 changed files with 642 additions and 482 deletions
7
.gitignore
vendored
7
.gitignore
vendored
|
|
@ -1,5 +1,6 @@
|
|||
*.so
|
||||
*.h
|
||||
*.swp
|
||||
*/__pycache__
|
||||
flask/static
|
||||
*/output.png
|
||||
__pycache__
|
||||
static
|
||||
*output.png
|
||||
|
|
|
|||
3
.gitmodules
vendored
3
.gitmodules
vendored
|
|
@ -1,3 +0,0 @@
|
|||
[submodule "flask/epson"]
|
||||
path = flask/epson
|
||||
url = https://mid-kid.root.sx/git/mid-kid/epson.git
|
||||
2
Makefile
2
Makefile
|
|
@ -1,4 +1,4 @@
|
|||
build:
|
||||
cd faxmachine &&\
|
||||
cd epson/faxmachine &&\
|
||||
go build -o ../library_bridge.so -buildmode=c-shared ../library_bridge.go
|
||||
|
||||
|
|
|
|||
209
app.py
Normal file
209
app.py
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
from flask import Flask, render_template, request, redirect, url_for, session, send_from_directory
|
||||
from PIL import Image
|
||||
from gen_image import gen_image
|
||||
from printer_info import *
|
||||
from datetime import datetime
|
||||
from print import *
|
||||
from format_image import *
|
||||
from file_handler import *
|
||||
from colorama import Fore, Style
|
||||
import os
|
||||
|
||||
generated_image_filename = "generated.png"
|
||||
uploaded_image_filename = "uploaded" #extention is depended on uploaded file format
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
app.secret_key = "blahaj"
|
||||
|
||||
templates = {
|
||||
"DNH": {
|
||||
"text1": {
|
||||
"string": "Do Not Hack",
|
||||
"size": 50,
|
||||
"pos": 0
|
||||
},
|
||||
"text2": {
|
||||
"string": "bottom text",
|
||||
"size": 25,
|
||||
"pos": 55
|
||||
},
|
||||
"cut": True
|
||||
},
|
||||
"Food": {
|
||||
"text1": {
|
||||
"string": "Nickname",
|
||||
"size": 50,
|
||||
"pos": 0
|
||||
},
|
||||
"text2": {
|
||||
"string": "",
|
||||
"size": 25,
|
||||
"pos": 55
|
||||
},
|
||||
"cut": True
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@app.route('/', methods=['GET', 'POST'])
|
||||
def base():
|
||||
check_for_new_user(session)
|
||||
return render_template('base.html')
|
||||
|
||||
|
||||
# gets triggered when the user_data folder for the user is created
|
||||
@new_user_handler
|
||||
def on_new_user(session):
|
||||
print(Fore.BLUE + "new user with ID:", session["ID"], Style.RESET_ALL)
|
||||
|
||||
session["text image path"] = None
|
||||
session["uploaded image path"] = None
|
||||
session["cut"] = True
|
||||
session["text1"] = templates["DNH"]["text1"]
|
||||
session["text2"] = templates["DNH"]["text2"]
|
||||
session["cut"] = templates["DNH"]["cut"]
|
||||
|
||||
|
||||
|
||||
@app.route('/image', methods=['GET', 'POST'])
|
||||
def image():
|
||||
check_for_new_user(session)
|
||||
|
||||
if request.method == 'POST':
|
||||
if 'image' not in request.files:
|
||||
return redirect(request.url)
|
||||
file = request.files['image']
|
||||
|
||||
if file.filename == '':
|
||||
return redirect(request.url)
|
||||
if file:
|
||||
extension = os.path.splitext(file.filename)[1]
|
||||
session["uploaded image path"] = uploaded_image_filename+extension
|
||||
|
||||
filepath = get_file_path(session, session["uploaded image path"])
|
||||
file.save(filepath)
|
||||
|
||||
format_image(filepath).save(filepath)
|
||||
|
||||
return render_template('image.html', filename=session["uploaded image path"], cut=session["cut"])
|
||||
|
||||
|
||||
#TODO: maybe merge some stuff with text-print
|
||||
@app.route('/image-print', methods=['GET', 'POST'])
|
||||
def image_print():
|
||||
check_for_new_user(session)
|
||||
|
||||
if session["uploaded image path"] == None:
|
||||
print(Fore.YELLOW + "Warning, file doesn't exist" + Style.RESET_ALL)
|
||||
return render_template('image.html', filename=session["uploaded image path"], cut=session["cut"])
|
||||
|
||||
if request.method == 'POST':
|
||||
image = format_image_to_label(get_file_path(session, session["uploaded image path"]))
|
||||
print_image(image)
|
||||
|
||||
if 'cut' in request.form:
|
||||
print("printing and cutting")
|
||||
cut_paper()
|
||||
session["cut"] = True;
|
||||
else:
|
||||
print("printing")
|
||||
session["cut"] = False;
|
||||
|
||||
return render_template('image.html', filename=session["uploaded image path"], cut=session["cut"])
|
||||
|
||||
|
||||
@app.route('/text-template', methods=['GET', 'POST'])
|
||||
def text_template():
|
||||
check_for_new_user(session)
|
||||
|
||||
if request.method == 'POST':
|
||||
template = templates[request.form["template"]]
|
||||
if request.form["template"] == "Food":
|
||||
template["text2"]["string"] = datetime.now().strftime('%Y-%m-%d')
|
||||
|
||||
session["text1"] = template["text1"]
|
||||
session["text2"] = template["text2"]
|
||||
session["cut"] = template["cut"]
|
||||
|
||||
return render_template('text.html', filename="text.png", text1=session["text1"], text2=session["text2"], cut=session["cut"])
|
||||
|
||||
|
||||
@app.route('/text-form', methods=['GET', 'POST'])
|
||||
def text_form():
|
||||
check_for_new_user(session)
|
||||
|
||||
if request.method == 'POST':
|
||||
session["text1"] = {
|
||||
"string": request.form["string1"],
|
||||
"size": int(request.form["size1"]),
|
||||
"pos": int(request.form["pos1"]),
|
||||
}
|
||||
session["text2"] = {
|
||||
"string": request.form["string2"],
|
||||
"size": int(request.form["size2"]),
|
||||
"pos": int(request.form["pos2"]),
|
||||
}
|
||||
|
||||
img = gen_image(label_width, session["text1"], session["text2"])
|
||||
|
||||
get_folder_path(session)
|
||||
|
||||
session["text image path"] = generated_image_filename
|
||||
img.save(get_file_path(session, session["text image path"]))
|
||||
print()
|
||||
print(session["text image path"])
|
||||
|
||||
return render_template('text.html', filename=session["text image path"], text1=session["text1"], text2=session["text2"], cut=session["cut"])
|
||||
|
||||
|
||||
@app.route('/text', methods=['GET', 'POST'])
|
||||
def text():
|
||||
check_for_new_user(session)
|
||||
|
||||
return render_template('text.html', filename=session["text image path"], text1=session["text1"], text2=session["text2"], cut=session["cut"])
|
||||
|
||||
|
||||
@app.route('/text-print', methods=['GET', 'POST'])
|
||||
def text_print():
|
||||
check_for_new_user(session)
|
||||
|
||||
if request.method == 'POST':
|
||||
if session["text image path"] == None:
|
||||
print(Fore.YELLOW + "Warning, file doesn't exist" + Style.RESET_ALL)
|
||||
return render_template('text.html', filename=session["text image path"], text1=session["text1"], text2=session["text2"], cut=session["cut"])
|
||||
|
||||
|
||||
image = format_image_to_label(get_file_path(session, session["text image path"]))
|
||||
print_image(image)
|
||||
|
||||
if 'cut' in request.form:
|
||||
print("printing and cutting")
|
||||
cut_paper()
|
||||
session["cut"] = True;
|
||||
else:
|
||||
print("printing")
|
||||
session["cut"] = False;
|
||||
|
||||
return render_template('text.html', filename=session["text image path"], text1=session["text1"], text2=session["text2"], cut=session["cut"])
|
||||
|
||||
|
||||
@app.route('/user_data/<filename>')
|
||||
def user_data(filename):
|
||||
return send_from_directory(get_folder_path(session), filename)
|
||||
|
||||
|
||||
@app.route('/uploads/<filename>')
|
||||
def uploaded_file(filename):
|
||||
return redirect(url_for('static', filename='uploads/' + filename))
|
||||
|
||||
|
||||
@app.route('/text/<filename>')
|
||||
def generated_file(filename):
|
||||
return redirect(url_for('static', filename='text/' + filename))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(debug=True)
|
||||
|
||||
128
epson/library_bridge.go
Normal file
128
epson/library_bridge.go
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
_ "time"
|
||||
"image"
|
||||
_ "image/gif"
|
||||
_ "image/jpeg"
|
||||
_ "image/png"
|
||||
"log"
|
||||
"unsafe"
|
||||
"bytes"
|
||||
|
||||
_ "github.com/jbuchbinder/gopnm"
|
||||
_ "golang.org/x/image/webp"
|
||||
|
||||
"git.sr.ht/~guacamolie/faxmachine/escpos"
|
||||
"git.sr.ht/~guacamolie/faxmachine/escpos/protocol"
|
||||
|
||||
)
|
||||
|
||||
import "C"
|
||||
|
||||
const red = "\033[31m"
|
||||
const reset = "\033[0m"
|
||||
|
||||
var err error
|
||||
|
||||
func printer_init(path string, speed int) (*escpos.Printer, error) {
|
||||
p, err := escpos.StartUSBPrinter(path, protocol.TMT88IV, escpos.FlagNone)
|
||||
if err != nil {
|
||||
fmt.Printf("%sError: failed to start printer: %v%s\n", red, err, reset)
|
||||
return p, err
|
||||
}
|
||||
|
||||
if err := p.EnableASB(protocol.ASBReportAll); err != nil {
|
||||
p.Close()
|
||||
fmt.Printf("%sError: failed enable ASB: %v%s\n", red, err, reset)
|
||||
return p, err
|
||||
}
|
||||
|
||||
go func() {
|
||||
for status := range p.ASBStatus() {
|
||||
log.Printf("received ASB status: %#v\n", status)
|
||||
}
|
||||
}()
|
||||
|
||||
if err := p.SetPrintSpeed(speed); err != nil {
|
||||
fmt.Printf("%sError: Failed to set print speed: %v%s\n", red, err, reset)
|
||||
return p, err
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func printer_close(p *escpos.Printer) (error) {
|
||||
if err = p.Wait(); err != nil {
|
||||
fmt.Printf("%sError: Failed to print: %v%s\n", red, err, reset)
|
||||
return err
|
||||
}
|
||||
if err = p.Close(); err != nil {
|
||||
fmt.Printf("%sError: Failed close printer: %v%s\n", red, err, reset)
|
||||
return err
|
||||
}
|
||||
fmt.Print("printer closed\n");
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//export cut
|
||||
func cut(path *C.char) {
|
||||
fmt.Print("Starting cut\n");
|
||||
p, err := printer_init(C.GoString(path), 1)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = p.CutPaper(); err != nil {
|
||||
fmt.Printf("%sError: Failed to cut paper: %v%s\n", red, err, reset)
|
||||
return
|
||||
}
|
||||
|
||||
printer_close(p)
|
||||
}
|
||||
|
||||
//export print_text
|
||||
func print_text(path *C.char, str *C.char) {
|
||||
p, err := printer_init(C.GoString(path), 1)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprint(p, C.GoString(str))
|
||||
fmt.Fprint(p, "\n")
|
||||
|
||||
printer_close(p)
|
||||
}
|
||||
|
||||
//export print_image
|
||||
func print_image(path *C.char, data *C.uchar, size C.int) {
|
||||
p, err := printer_init(C.GoString(path), 1)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
for status := range p.ASBStatus() {
|
||||
log.Printf("received ASB status: %#v\n", status)
|
||||
}
|
||||
}()
|
||||
|
||||
byteData := C.GoBytes(unsafe.Pointer(data), C.int(size))
|
||||
imgReader := bytes.NewReader(byteData)
|
||||
img, _, err := image.Decode(imgReader)
|
||||
if err != nil {
|
||||
fmt.Printf("%sError: Failed get decode image: %v%s\n", red, err, reset)
|
||||
return
|
||||
}
|
||||
if err := p.PrintImage(img); err != nil {
|
||||
fmt.Printf("%sError: Failed print image: %v%s\n", red, err, reset)
|
||||
return
|
||||
}
|
||||
|
||||
printer_close(p)
|
||||
}
|
||||
|
||||
|
||||
func main() {}
|
||||
76
file_handler.py
Normal file
76
file_handler.py
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
import random
|
||||
import os
|
||||
import shutil
|
||||
import time
|
||||
|
||||
user_data_lifetime_seconds = 2*60*60
|
||||
data_path = "user_data"
|
||||
users = dict([])
|
||||
|
||||
if os.path.exists(data_path):
|
||||
shutil.rmtree(data_path)
|
||||
|
||||
def delete_old_users(session):
|
||||
for ID in users.copy():
|
||||
elapsed = time.time() - users[ID]["last accessed"]
|
||||
|
||||
if (elapsed > user_data_lifetime_seconds):
|
||||
print(ID, "Hasn't accessed their files in", elapsed, "seconds, removing all user data")
|
||||
|
||||
shutil.rmtree(users[ID]["path"])
|
||||
del users[ID]
|
||||
|
||||
new_user_handler_func = None
|
||||
|
||||
|
||||
def new_user_handler(func):
|
||||
global new_user_handler_func
|
||||
new_user_handler_func = func
|
||||
return func
|
||||
|
||||
|
||||
def new_user(session):
|
||||
while(True):
|
||||
ID = random.randint(0, 100000000)
|
||||
if not ID in users:
|
||||
break
|
||||
|
||||
users[ID] = {
|
||||
"path": data_path + "/" + str(ID),
|
||||
"last accessed": time.time()
|
||||
}
|
||||
|
||||
session["ID"] = ID
|
||||
|
||||
os.makedirs(users[session["ID"]]["path"])
|
||||
|
||||
if new_user_handler_func:
|
||||
new_user_handler_func(session)
|
||||
|
||||
|
||||
def get_file_path(session, filename):
|
||||
return os.path.join(get_folder_path(session), filename)
|
||||
|
||||
|
||||
def file_exists(session, filename):
|
||||
return os.path.exists(get_file_path(session, filename))
|
||||
|
||||
|
||||
def check_for_new_user(session):
|
||||
if not "ID" in session:
|
||||
new_user(session)
|
||||
elif not session["ID"] in users:
|
||||
new_user(session)
|
||||
|
||||
delete_old_users(session)
|
||||
|
||||
|
||||
def get_folder_path(session):
|
||||
check_for_new_user(session)
|
||||
ID = session["ID"]
|
||||
user = users[ID]
|
||||
|
||||
user["last accessed"] = time.time()
|
||||
|
||||
return user["path"]
|
||||
|
||||
157
flask/app.py
157
flask/app.py
|
|
@ -1,157 +0,0 @@
|
|||
from flask import Flask, render_template, request, redirect, url_for, session
|
||||
from PIL import Image
|
||||
from gen_image import gen_image
|
||||
from printer_info import *
|
||||
from datetime import datetime
|
||||
from print import *
|
||||
from format_image import *
|
||||
import os
|
||||
|
||||
valid_height=800
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config['UPLOAD_FOLDER'] = 'static/uploads'
|
||||
app.config['TEXT_FOLDER'] = 'static/text'
|
||||
app.secret_key = "blahaj"
|
||||
|
||||
# Ensure the upload directory exists
|
||||
if not os.path.exists(app.config['UPLOAD_FOLDER']):
|
||||
os.makedirs(app.config['UPLOAD_FOLDER'])
|
||||
if not os.path.exists(app.config['TEXT_FOLDER']):
|
||||
os.makedirs(app.config['TEXT_FOLDER'])
|
||||
|
||||
|
||||
@app.route('/image', methods=['GET', 'POST'])
|
||||
def image():
|
||||
if request.method == 'POST':
|
||||
if 'image' not in request.files:
|
||||
return redirect(request.url)
|
||||
file = request.files['image']
|
||||
|
||||
if file.filename == '':
|
||||
return redirect(request.url)
|
||||
if file:
|
||||
extension = os.path.splitext(file.filename)[1]
|
||||
filepath = os.path.join(app.config['UPLOAD_FOLDER'], "upload" + extension)
|
||||
|
||||
file.save(filepath)
|
||||
|
||||
# Process image #
|
||||
height = Image.open(filepath).height
|
||||
|
||||
valid = False
|
||||
if height == valid_height:
|
||||
valid = True
|
||||
#################
|
||||
|
||||
return render_template('image.html', filename="upload"+extension, height=height, valid=valid)
|
||||
|
||||
return render_template('image.html')
|
||||
|
||||
templates = {
|
||||
"DNH": {
|
||||
"text1": {
|
||||
"string": "Do Not Hack",
|
||||
"size": 50,
|
||||
"pos": 0
|
||||
},
|
||||
"text2": {
|
||||
"string": "bottom text",
|
||||
"size": 25,
|
||||
"pos": 55
|
||||
},
|
||||
"cut": True
|
||||
},
|
||||
"Food": {
|
||||
"text1": {
|
||||
"string": "Nickname",
|
||||
"size": 50,
|
||||
"pos": 0
|
||||
},
|
||||
"text2": {
|
||||
"string": "",
|
||||
"size": 25,
|
||||
"pos": 55
|
||||
},
|
||||
"cut": True
|
||||
}
|
||||
}
|
||||
|
||||
@app.route('/text-template', methods=['GET', 'POST'])
|
||||
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')
|
||||
|
||||
session["text1"] = template["text1"]
|
||||
session["text2"] = template["text2"]
|
||||
session["cut"] = template["cut"]
|
||||
|
||||
return render_template('text.html', filename="text.png", text1=session["text1"], text2=session["text2"], cut=session["cut"])
|
||||
|
||||
return render_template('text.html', filename="text.png", text1=session["text1"], text2=session["text2"], cut=session["cut"])
|
||||
|
||||
@app.route('/text-form', methods=['GET', 'POST'])
|
||||
def text_form():
|
||||
if request.method == 'POST':
|
||||
session["text1"] = {
|
||||
"string": request.form["string1"],
|
||||
"size": int(request.form["size1"]),
|
||||
"pos": int(request.form["pos1"]),
|
||||
}
|
||||
session["text2"] = {
|
||||
"string": request.form["string2"],
|
||||
"size": int(request.form["size2"]),
|
||||
"pos": int(request.form["pos2"]),
|
||||
}
|
||||
|
||||
img = gen_image(label_width, session["text1"], session["text2"])
|
||||
filepath = os.path.join(app.config['TEXT_FOLDER'], "text.png")
|
||||
img.save(filepath)
|
||||
print(session)
|
||||
|
||||
return render_template('text.html', filename="text.png", text1=session["text1"], text2=session["text2"], cut=session["cut"])
|
||||
|
||||
return render_template('text.html', filename="text.png", text1=session["text1"], text2=session["text2"], cut=session["cut"])
|
||||
|
||||
|
||||
|
||||
@app.route('/text', methods=['GET', 'POST'])
|
||||
def text():
|
||||
session["text1"] = templates["DNH"]["text1"]
|
||||
session["text2"] = templates["DNH"]["text2"]
|
||||
session["cut"] = templates["DNH"]["cut"]
|
||||
return render_template('text.html', filename="text.png", text1=session["text1"], text2=session["text2"], cut=session["cut"])
|
||||
|
||||
|
||||
@app.route('/text-print', methods=['GET', 'POST'])
|
||||
def tex_print():
|
||||
if request.method == 'POST':
|
||||
image = format_image("static/text/text.png")
|
||||
print_image(image)
|
||||
|
||||
if 'cut' in request.form:
|
||||
print("printing and cutting")
|
||||
cut_paper()
|
||||
session["cut"] = True;
|
||||
else:
|
||||
print("printing")
|
||||
session["cut"] = False;
|
||||
|
||||
return render_template('text.html', filename="text.png", text1=session["text1"], text2=session["text2"], cut=session["cut"])
|
||||
|
||||
return render_template('text.html', filename="text.png", text1=session["text1"], text2=session["text2"], cut=session["cut"])
|
||||
|
||||
|
||||
@app.route('/uploads/<filename>')
|
||||
def uploaded_file(filename):
|
||||
return redirect(url_for('static', filename='uploads/' + filename))
|
||||
|
||||
@app.route('/text/<filename>')
|
||||
def generated_file(filename):
|
||||
return redirect(url_for('static', filename='text/' + filename))
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(debug=True)
|
||||
|
||||
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 37ab1729e6cdb212619bd4c12097ef0d926c0c2c
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
from PIL import Image
|
||||
|
||||
def format_image(path):
|
||||
image = Image.open(path)
|
||||
image = image.rotate(-90, expand=True)
|
||||
|
||||
new_image = Image.new('1', (512, image.height), 1)
|
||||
new_image.paste(image, (0, 0))
|
||||
|
||||
return new_image
|
||||
|
||||
#format_image("static/text/text.png").save("output.png")
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
dev_path="/dev/usb/lp0"
|
||||
from format_image import format_image
|
||||
from PIL import Image
|
||||
import os
|
||||
|
||||
def print_text(text):
|
||||
stream = open('/dev/usb/lp0', 'wb')
|
||||
stream.write(text.encode('utf-8'))
|
||||
stream.close()
|
||||
|
||||
def cut_paper():
|
||||
stream = open('/dev/usb/lp0', 'wb')
|
||||
stream.write(b'\x1DV\x41\0')
|
||||
stream.close()
|
||||
|
||||
def print_image(image):
|
||||
image.save("/tmp/image.png")
|
||||
|
||||
# I'm sorry
|
||||
os.system("cd epson/; ./print_image.sh /tmp/image.png; cd ..")
|
||||
|
||||
#image = format_image("static/text/text.png")
|
||||
#image.save("/tmp/image.png")
|
||||
#print_image(image)
|
||||
#cut_paper()
|
||||
|
||||
|
||||
|
|
@ -1 +0,0 @@
|
|||
label_width = 100
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Image Upload</title>
|
||||
|
||||
<style>
|
||||
.container {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
}
|
||||
.box {
|
||||
flex: 1;
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
margin: 1px;
|
||||
}
|
||||
</style>
|
||||
|
||||
{% block head %}
|
||||
{% endblock %}
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="box">Text</div>
|
||||
<div class="box">Image</div>
|
||||
<div class="box">QR</div>
|
||||
<div class="box">Barcode</div>
|
||||
</div>
|
||||
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Upload Image</h1>
|
||||
<form method="POST" enctype="multipart/form-data">
|
||||
<input type="file" name="image">
|
||||
<input type="submit" value="Upload">
|
||||
</form>
|
||||
|
||||
{% if filename %}
|
||||
<h2>Uploaded Image:</h2>
|
||||
<img src="{{ url_for('uploaded_file', filename=filename) }}" alt="Uploaded Image" style="max-width:300px;">
|
||||
{% if valid %}
|
||||
<p style="color: green;">Height: {{ height }} px</p>
|
||||
{% else %}
|
||||
<p style="color: red;">Height: {{ height }} px</p>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
27
format_image.py
Normal file
27
format_image.py
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
from PIL import Image
|
||||
from printer_info import *
|
||||
|
||||
def format_image_to_label(path):
|
||||
image = Image.open(path)
|
||||
image = image.rotate(-90, expand=True)
|
||||
|
||||
new_image = Image.new('1', (printer_width, image.height), 1)
|
||||
new_image.paste(image, (0, 0))
|
||||
|
||||
return new_image
|
||||
|
||||
def format_image(path):
|
||||
image = Image.open(path)
|
||||
|
||||
if (image.height != label_width):
|
||||
print("resizing image")
|
||||
new_height = label_width
|
||||
new_width = int(label_width * image.width / image.height)
|
||||
|
||||
image = image.resize((new_width, new_height))
|
||||
|
||||
image = image.convert('1', dither=Image.FLOYDSTEINBERG)
|
||||
return image
|
||||
|
||||
|
||||
#format_image("static/uploads/upload.png").save("output.png")
|
||||
|
|
@ -2,9 +2,11 @@ from PIL import Image, ImageDraw, ImageFont
|
|||
from colorama import Fore, Style
|
||||
from printer_info import *
|
||||
|
||||
font = "resources/ComicMono.ttf"
|
||||
|
||||
def gen_image(height, text1, text2):
|
||||
font1 = ImageFont.truetype("ComicMono.ttf", size=text1["size"])
|
||||
font2 = ImageFont.truetype("ComicMono.ttf", size=text2["size"])
|
||||
font1 = ImageFont.truetype(font, size=text1["size"])
|
||||
font2 = ImageFont.truetype(font, size=text2["size"])
|
||||
|
||||
text1["string"] = text1["string"].replace("\r\n", "\n")
|
||||
text2["string"] = text2["string"].replace("\r\n", "\n")
|
||||
|
|
@ -1,123 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
_ "image"
|
||||
_ "image/gif"
|
||||
_ "image/jpeg"
|
||||
_ "image/png"
|
||||
"log"
|
||||
_ "os"
|
||||
|
||||
_ "github.com/jbuchbinder/gopnm"
|
||||
_ "golang.org/x/image/webp"
|
||||
|
||||
_ "github.com/lestrrat-go/dither"
|
||||
_ "github.com/nfnt/resize"
|
||||
|
||||
"git.sr.ht/~guacamolie/faxmachine/escpos"
|
||||
"git.sr.ht/~guacamolie/faxmachine/escpos/protocol"
|
||||
)
|
||||
|
||||
import "C"
|
||||
|
||||
var err error
|
||||
|
||||
func printer_init(path string, speed int) (*escpos.Printer) {
|
||||
p, err := escpos.StartUSBPrinter(path, protocol.TMT88IV, escpos.FlagNone)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to start printer: %v", err)
|
||||
}
|
||||
|
||||
if err := p.EnableASB(protocol.ASBReportAll); err != nil {
|
||||
p.Close()
|
||||
log.Fatalf("failed to enable ASB: %v", err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
for status := range p.ASBStatus() {
|
||||
log.Printf("received ASB status: %#v\n", status)
|
||||
}
|
||||
}()
|
||||
|
||||
if err := p.SetPrintSpeed(speed); err != nil {
|
||||
log.Fatalf("failed to set print speed: %v\n", err)
|
||||
}
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
//export cut
|
||||
func cut(path *C.char) {
|
||||
p := printer_init(C.GoString(path), 1)
|
||||
|
||||
if err = p.CutPaper(); err != nil {
|
||||
log.Fatalf("failed to cut paper: %v", err)
|
||||
}
|
||||
|
||||
if err = p.Wait(); err != nil {
|
||||
log.Fatalf("failed to print: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
//export print_text
|
||||
func print_text(path *C.char, str *C.char) {
|
||||
p := printer_init(C.GoString(path), 1)
|
||||
|
||||
fmt.Fprint(p, C.GoString(str))
|
||||
fmt.Fprint(p, "\n")
|
||||
|
||||
if err = p.Wait(); err != nil {
|
||||
log.Fatalf("failed to print: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func printimg(path *C.char) {
|
||||
p, err := escpos.StartUSBPrinter("/dev/usb/lp0", protocol.TMT88IV, escpos.FlagNone)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to start printer: %v\n", err)
|
||||
}
|
||||
defer p.Close()
|
||||
|
||||
if err := p.EnableASB(protocol.ASBReportAll); err != nil {
|
||||
log.Fatalf("failed to enable ASB: %v\n", err)
|
||||
}
|
||||
go func() {
|
||||
for status := range p.ASBStatus() {
|
||||
log.Printf("received ASB status: %#v\n", status)
|
||||
}
|
||||
}()
|
||||
|
||||
//img, _, err := image.Decode(os.Stdin)
|
||||
//if err != nil {
|
||||
// log.Fatalf("failed to get decode image: %v\n", err)
|
||||
//}
|
||||
//img = resize.Resize(512, 0, img, resize.Lanczos3)
|
||||
//img = dither.Monochrome(dither.FloydSteinberg, img, 1.18)
|
||||
|
||||
// if err := p.SetEncoding(escpos.CharPagePC427); err != nil {
|
||||
// log.Fatalf("failed to set encoding: %v\n", err)
|
||||
// }
|
||||
if err := p.SetPrintSpeed(1); err != nil {
|
||||
log.Fatalf("failed to set print speed: %v\n", err)
|
||||
}
|
||||
//if err := p.PrintImage(img); err != nil {
|
||||
// log.Fatalf("failed to print image: %v\n", err)
|
||||
//}
|
||||
|
||||
//if len(os.Args) > 2 && os.Args[2] != "" {
|
||||
// fmt.Fprintf(p, "\n%s\n", os.Args[2])
|
||||
//}
|
||||
|
||||
fmt.Fprint(p, "\n\n\n")
|
||||
|
||||
if err := p.CutPaper(); err != nil {
|
||||
log.Fatalf("failed to cut paper: %v", err)
|
||||
}
|
||||
|
||||
if err := p.Wait(); err != nil {
|
||||
log.Fatalf("failed to print: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {}
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
/* Code generated by cmd/cgo; DO NOT EDIT. */
|
||||
|
||||
/* package command-line-arguments */
|
||||
|
||||
|
||||
#line 1 "cgo-builtin-export-prolog"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifndef GO_CGO_EXPORT_PROLOGUE_H
|
||||
#define GO_CGO_EXPORT_PROLOGUE_H
|
||||
|
||||
#ifndef GO_CGO_GOSTRING_TYPEDEF
|
||||
typedef struct { const char *p; ptrdiff_t n; } _GoString_;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/* Start of preamble from import "C" comments. */
|
||||
|
||||
|
||||
|
||||
|
||||
/* End of preamble from import "C" comments. */
|
||||
|
||||
|
||||
/* Start of boilerplate cgo prologue. */
|
||||
#line 1 "cgo-gcc-export-header-prolog"
|
||||
|
||||
#ifndef GO_CGO_PROLOGUE_H
|
||||
#define GO_CGO_PROLOGUE_H
|
||||
|
||||
typedef signed char GoInt8;
|
||||
typedef unsigned char GoUint8;
|
||||
typedef short GoInt16;
|
||||
typedef unsigned short GoUint16;
|
||||
typedef int GoInt32;
|
||||
typedef unsigned int GoUint32;
|
||||
typedef long long GoInt64;
|
||||
typedef unsigned long long GoUint64;
|
||||
typedef GoInt64 GoInt;
|
||||
typedef GoUint64 GoUint;
|
||||
typedef size_t GoUintptr;
|
||||
typedef float GoFloat32;
|
||||
typedef double GoFloat64;
|
||||
#ifdef _MSC_VER
|
||||
#include <complex.h>
|
||||
typedef _Fcomplex GoComplex64;
|
||||
typedef _Dcomplex GoComplex128;
|
||||
#else
|
||||
typedef float _Complex GoComplex64;
|
||||
typedef double _Complex GoComplex128;
|
||||
#endif
|
||||
|
||||
/*
|
||||
static assertion to make sure the file is being used on architecture
|
||||
at least with matching size of GoInt.
|
||||
*/
|
||||
typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];
|
||||
|
||||
#ifndef GO_CGO_GOSTRING_TYPEDEF
|
||||
typedef _GoString_ GoString;
|
||||
#endif
|
||||
typedef void *GoMap;
|
||||
typedef void *GoChan;
|
||||
typedef struct { void *t; void *v; } GoInterface;
|
||||
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
|
||||
|
||||
#endif
|
||||
|
||||
/* End of boilerplate cgo prologue. */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern void cut(char* path);
|
||||
extern void print_text(char* path, char* str);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
48
print.py
Normal file
48
print.py
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
dev_path="/dev/usb/lp0"
|
||||
from format_image import *
|
||||
from PIL import Image
|
||||
import ctypes
|
||||
import io
|
||||
import time
|
||||
|
||||
printer = ctypes.CDLL('./epson/library_bridge.so')
|
||||
|
||||
def print_text(text):
|
||||
time.sleep(0.1)
|
||||
|
||||
try:
|
||||
printer.print_text(b"/dev/usb/lp0", text.encode('utf-8'))
|
||||
except Exception as e:
|
||||
print(f"An error occurred: {e}")
|
||||
|
||||
def cut_paper():
|
||||
time.sleep(0.1)
|
||||
|
||||
try:
|
||||
printer.cut(b"/dev/usb/lp0")
|
||||
except Exception as e:
|
||||
print(f"An error occurred: {e}")
|
||||
|
||||
|
||||
def print_image(image):
|
||||
time.sleep(0.1)
|
||||
|
||||
Bytes = io.BytesIO()
|
||||
image.save(Bytes, format='PNG')
|
||||
Bytes = Bytes.getvalue()
|
||||
|
||||
|
||||
try:
|
||||
printer.print_image(b"/dev/usb/lp0", Bytes, len(Bytes))
|
||||
except Exception as e:
|
||||
print(f"An error occurred: {e}")
|
||||
|
||||
# I'm sorry
|
||||
#os.system("cd epson/; ./print_image.sh /tmp/image.png; cd ..")
|
||||
|
||||
#image = format_image("static/text/text.png")
|
||||
#image.save("/tmp/image.png")
|
||||
#print_image(image)
|
||||
#cut_paper()
|
||||
|
||||
|
||||
2
printer_info.py
Normal file
2
printer_info.py
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
label_width = 100
|
||||
printer_width = 512
|
||||
22
resources/LICENSE
Normal file
22
resources/LICENSE
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
MIT License
|
||||
|
||||
Original work Copyright (c) 2018 Shannon Miwa
|
||||
Modified work Copyright (c) 2019 dtinth
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
94
templates/base.html
Normal file
94
templates/base.html
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Image Upload</title>
|
||||
|
||||
<style>
|
||||
body {
|
||||
background-color: #f8f9fa;
|
||||
font-family: Arial, sans-serif;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.box {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
margin: 1px;
|
||||
padding: 20px;
|
||||
color: white;
|
||||
background-color: #ff69b4; /* Pink */
|
||||
border: 2px solid #55cdfc; /* Light blue */
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
|
||||
.box:hover {
|
||||
background-color: #f7a8b8; /* Softer pink on hover */
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
label {
|
||||
font-weight: bold;
|
||||
color: #ff69b4;
|
||||
}
|
||||
|
||||
input[type="submit"], select, textarea, input[type="number"] {
|
||||
background-color: #55cdfc;
|
||||
border: none;
|
||||
color: white;
|
||||
padding: 10px;
|
||||
margin: 10px 0;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
input[type="submit"]:hover, input[type="number"]:hover, select:hover, textarea:hover {
|
||||
background-color: #f7a8b8;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: #ff69b4;
|
||||
}
|
||||
|
||||
img {
|
||||
border: 3px solid #55cdfc;
|
||||
}
|
||||
|
||||
form {
|
||||
background-color: #f8f9fa;
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
border: 2px solid #ff69b4;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
{% block head %}
|
||||
{% endblock %}
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<a href="text" class="box">
|
||||
<div>Text</div>
|
||||
</a>
|
||||
<a href="image" class="box">
|
||||
<div>Image</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
25
templates/image.html
Normal file
25
templates/image.html
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Upload Image</h1>
|
||||
<form method="POST" action="/image" enctype="multipart/form-data">
|
||||
<input type="file" name="image">
|
||||
<input type="submit" value="Upload">
|
||||
</form>
|
||||
|
||||
{% if filename %}
|
||||
<br><br>
|
||||
<img src="user_data/{{filename}}" alt="Uploaded image">
|
||||
|
||||
<form method="POST" action="/image-print">
|
||||
{% if cut %}
|
||||
<input type="checkbox" name="cut" value="cut" checked>
|
||||
{%else %}
|
||||
<input type="checkbox" name="cut" value="cut">
|
||||
{%endif%}
|
||||
<label for="cut"> Cut paper after printing </label><br>
|
||||
|
||||
<input type="submit" value="Print" style="font-size: 2em; padding: 20px 40px;">
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
|
|
@ -33,8 +33,7 @@
|
|||
|
||||
{% if filename %}
|
||||
<br><br>
|
||||
<img style="border: 1px solid blueviolet;"
|
||||
src="{{ url_for('generated_file', filename=filename) }}" alt="Uploaded Image">
|
||||
<img src="user_data/{{filename}}" alt="Generated Image">
|
||||
|
||||
<form method="POST" action="/text-print">
|
||||
{% if cut %}
|
||||
|
|
@ -44,7 +43,7 @@
|
|||
{%endif%}
|
||||
<label for="cut"> Cut paper after printing </label><br>
|
||||
|
||||
<input type="submit" value="Print">
|
||||
<input type="submit" value="Print" style="font-size: 2em; padding: 20px 40px;">
|
||||
</form>
|
||||
{% endif %}
|
||||
|
||||
9
test.py
9
test.py
|
|
@ -1,9 +0,0 @@
|
|||
import ctypes
|
||||
|
||||
mylib = ctypes.CDLL('./library_bridge.so')
|
||||
|
||||
mylib.print_text("/dev/usb/lp0".encode('utf-8'), "test\n".encode('utf-8'))
|
||||
mylib.print_text("/dev/usb/lp0".encode('utf-8'), "test\n".encode('utf-8'))
|
||||
mylib.print_text("/dev/usb/lp0".encode('utf-8'), "test\n".encode('utf-8'))
|
||||
|
||||
mylib.cut("/dev/usb/lp0".encode('utf-8'))
|
||||
Loading…
Reference in a new issue