Compare commits

...

10 commits

Author SHA1 Message Date
TT-392
864dcb8160 Implement hopeful fix for cutting issues 2024-09-18 00:11:43 +02:00
TT-392
cd4df840b6 Implement file handler with separated data per user 2024-09-18 00:11:38 +02:00
TT-392
c4436d040b Small fixes in app.py 2024-09-16 17:45:11 +02:00
TT-392
117efa3589 Add license file for comicmono 2024-09-15 21:19:51 +02:00
TT-392
80bf0cf4e8 Some UI changes 2024-09-15 21:00:27 +02:00
TT-392
1d0f5cf199 Fully integrate the go library and reorganize files 2024-09-15 20:53:03 +02:00
TT-392
78456105ac Working cut, text, and image through bridge 2024-09-15 19:55:11 +02:00
TT-392
10d218bfba Image printing works 2024-09-15 17:48:47 +02:00
TT-392
bd1c7ed477 Apply design puke 2024-09-15 17:48:36 +02:00
TT-392
29e254434f Mke clickable tabs 2024-09-15 13:53:54 +02:00
49 changed files with 642 additions and 482 deletions

7
.gitignore vendored
View file

@ -1,5 +1,6 @@
*.so *.so
*.h
*.swp *.swp
*/__pycache__ __pycache__
flask/static static
*/output.png *output.png

3
.gitmodules vendored
View file

@ -1,3 +0,0 @@
[submodule "flask/epson"]
path = flask/epson
url = https://mid-kid.root.sx/git/mid-kid/epson.git

View file

@ -1,4 +1,4 @@
build: build:
cd faxmachine &&\ cd epson/faxmachine &&\
go build -o ../library_bridge.so -buildmode=c-shared ../library_bridge.go go build -o ../library_bridge.so -buildmode=c-shared ../library_bridge.go

209
app.py Normal file
View 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
View 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
View 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"]

View file

@ -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

View file

@ -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")

View file

@ -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()

View file

@ -1 +0,0 @@
label_width = 100

View file

@ -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>

View file

@ -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
View 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")

View file

@ -2,9 +2,11 @@ from PIL import Image, ImageDraw, ImageFont
from colorama import Fore, Style from colorama import Fore, Style
from printer_info import * from printer_info import *
font = "resources/ComicMono.ttf"
def gen_image(height, text1, text2): def gen_image(height, text1, text2):
font1 = ImageFont.truetype("ComicMono.ttf", size=text1["size"]) font1 = ImageFont.truetype(font, size=text1["size"])
font2 = ImageFont.truetype("ComicMono.ttf", size=text2["size"]) font2 = ImageFont.truetype(font, size=text2["size"])
text1["string"] = text1["string"].replace("\r\n", "\n") text1["string"] = text1["string"].replace("\r\n", "\n")
text2["string"] = text2["string"].replace("\r\n", "\n") text2["string"] = text2["string"].replace("\r\n", "\n")

View file

@ -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() {}

View file

@ -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
View 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
View file

@ -0,0 +1,2 @@
label_width = 100
printer_width = 512

22
resources/LICENSE Normal file
View 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
View 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
View 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 %}

View file

@ -33,8 +33,7 @@
{% if filename %} {% if filename %}
<br><br> <br><br>
<img style="border: 1px solid blueviolet;" <img src="user_data/{{filename}}" alt="Generated Image">
src="{{ url_for('generated_file', filename=filename) }}" alt="Uploaded Image">
<form method="POST" action="/text-print"> <form method="POST" action="/text-print">
{% if cut %} {% if cut %}
@ -44,7 +43,7 @@
{%endif%} {%endif%}
<label for="cut"> Cut paper after printing </label><br> <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> </form>
{% endif %} {% endif %}

View file

@ -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'))