fix printing?
This commit is contained in:
parent
926aaefd85
commit
b3ebce60f4
3 changed files with 102 additions and 99 deletions
1
.envrc
Normal file
1
.envrc
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
use python autokanban
|
||||||
1
.python-version
Normal file
1
.python-version
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
3.13
|
||||||
199
app/main.py
199
app/main.py
|
|
@ -21,7 +21,7 @@ app.mount("/static", StaticFiles(directory="app/static"), name="static")
|
||||||
app.mount("/out", StaticFiles(directory="out"), name="out")
|
app.mount("/out", StaticFiles(directory="out"), name="out")
|
||||||
|
|
||||||
TASKS_FILE = Path("data/tasks.json")
|
TASKS_FILE = Path("data/tasks.json")
|
||||||
DEBUG_PRINT_TO_IMAGE = True # Set to True to enable card image generation instead of real printing
|
ENABLE_PHYSICAL_PRINTER = True # Set to False to skip physical printing (only generate preview images)
|
||||||
OUT_DIR = Path("out")
|
OUT_DIR = Path("out")
|
||||||
OUT_DIR.mkdir(exist_ok=True)
|
OUT_DIR.mkdir(exist_ok=True)
|
||||||
CARD_WIDTH_PX = 354 # 58mm * 300dpi / 25.4
|
CARD_WIDTH_PX = 354 # 58mm * 300dpi / 25.4
|
||||||
|
|
@ -140,109 +140,110 @@ def print_task(request: Request, task_id: str):
|
||||||
if task.id == task_id:
|
if task.id == task_id:
|
||||||
if (task.status == "approved") or (task.status == "printed" and request.session.get("admin")):
|
if (task.status == "approved") or (task.status == "printed" and request.session.get("admin")):
|
||||||
try:
|
try:
|
||||||
if DEBUG_PRINT_TO_IMAGE:
|
# Always generate preview image
|
||||||
# Generate styled card image with icon
|
img = Image.new("L", (CARD_WIDTH_PX, CARD_HEIGHT_PX), 255)
|
||||||
img = Image.new("L", (CARD_WIDTH_PX, CARD_HEIGHT_PX), 255)
|
draw = ImageDraw.Draw(img)
|
||||||
draw = ImageDraw.Draw(img)
|
draw.rectangle([(0,0),(CARD_WIDTH_PX-1,CARD_HEIGHT_PX-1)], outline=0, width=4)
|
||||||
draw.rectangle([(0,0),(CARD_WIDTH_PX-1,CARD_HEIGHT_PX-1)], outline=0, width=4)
|
|
||||||
|
|
||||||
# Robust font loading
|
# Robust font loading
|
||||||
font_title = load_font(FONT_BOLD, 36, font_label="title")
|
font_title = load_font(FONT_BOLD, 36, font_label="title")
|
||||||
font_label_f = load_font(FONT_BOLD, 18, font_label="label")
|
font_label_f = load_font(FONT_BOLD, 18, font_label="label")
|
||||||
font_text = load_font(FONT_REGULAR, 22, font_label="text")
|
font_text = load_font(FONT_REGULAR, 22, font_label="text")
|
||||||
font_icon = load_font(FA_FONT, 48, font_label="icon")
|
font_icon = load_font(FA_FONT, 48, font_label="icon")
|
||||||
|
|
||||||
# Prepare content for line wrapping
|
# Prepare content for line wrapping
|
||||||
import textwrap
|
import textwrap
|
||||||
y = 24
|
y = 24
|
||||||
max_text_width = CARD_WIDTH_PX - 40
|
max_text_width = CARD_WIDTH_PX - 40
|
||||||
content_lines = []
|
content_lines = []
|
||||||
if font_title:
|
if font_title:
|
||||||
wrapper = textwrap.TextWrapper(width=24)
|
wrapper = textwrap.TextWrapper(width=24)
|
||||||
lines = wrapper.wrap(task.content)
|
lines = wrapper.wrap(task.content)
|
||||||
for line in lines:
|
for line in lines:
|
||||||
# Check pixel width, wrap further if needed
|
# Check pixel width, wrap further if needed
|
||||||
while font_title.getlength(line) > max_text_width:
|
while font_title.getlength(line) > max_text_width:
|
||||||
# Reduce by one word at a time
|
# Reduce by one word at a time
|
||||||
split = line.rsplit(' ', 1)
|
split = line.rsplit(' ', 1)
|
||||||
if len(split) == 2:
|
if len(split) == 2:
|
||||||
content_lines.append(split[0])
|
content_lines.append(split[0])
|
||||||
line = split[1]
|
line = split[1]
|
||||||
else:
|
else:
|
||||||
# Single long word
|
# Single long word
|
||||||
content_lines.append(line[:20])
|
content_lines.append(line[:20])
|
||||||
line = line[20:]
|
line = line[20:]
|
||||||
content_lines.append(line)
|
content_lines.append(line)
|
||||||
else:
|
|
||||||
content_lines = textwrap.wrap(task.content, width=24)
|
|
||||||
|
|
||||||
# Center lines vertically
|
|
||||||
|
|
||||||
# Calculate text heights using getbbox
|
|
||||||
def get_text_height(font, text):
|
|
||||||
try:
|
|
||||||
bbox = font.getbbox(text)
|
|
||||||
return bbox[3] - bbox[1]
|
|
||||||
except Exception:
|
|
||||||
return 24
|
|
||||||
|
|
||||||
line_heights = [get_text_height(font_title, line) for line in content_lines]
|
|
||||||
total_text_height = sum(line_heights) + (len(content_lines)-1)*2
|
|
||||||
y = (CARD_HEIGHT_PX - total_text_height) // 2 - 10
|
|
||||||
for idx, line in enumerate(content_lines):
|
|
||||||
try:
|
|
||||||
w = font_title.getlength(line)
|
|
||||||
except Exception:
|
|
||||||
w = len(line) * 18
|
|
||||||
draw.text(((CARD_WIDTH_PX-w)//2, y), line, font=font_title, fill=0)
|
|
||||||
y += line_heights[idx] + 2
|
|
||||||
|
|
||||||
# User (centered below text)
|
|
||||||
user_label = f"Von: {task.user}"
|
|
||||||
try:
|
|
||||||
w = font_label_f.getlength(user_label)
|
|
||||||
user_h = get_text_height(font_label_f, user_label)
|
|
||||||
except Exception:
|
|
||||||
w = len(user_label) * 10
|
|
||||||
user_h = 18
|
|
||||||
draw.text(((CARD_WIDTH_PX-w)//2, y+8), user_label, font=font_label_f, fill=0)
|
|
||||||
y += user_h + 12
|
|
||||||
|
|
||||||
# Priority (centered below user)
|
|
||||||
prio_label = f"Priorität: {task.priority}"
|
|
||||||
try:
|
|
||||||
w = font_label_f.getlength(prio_label)
|
|
||||||
except Exception:
|
|
||||||
w = len(prio_label) * 10
|
|
||||||
draw.text(((CARD_WIDTH_PX-w)//2, y), prio_label, font=font_label_f, fill=0)
|
|
||||||
|
|
||||||
# Icon in lower right corner
|
|
||||||
icon = get_icon_for_task(task.content)
|
|
||||||
if font_icon:
|
|
||||||
icon_bbox = font_icon.getbbox(icon)
|
|
||||||
icon_w = icon_bbox[2] - icon_bbox[0]
|
|
||||||
icon_h = icon_bbox[3] - icon_bbox[1]
|
|
||||||
icon_x = CARD_WIDTH_PX - icon_w - 16
|
|
||||||
icon_y = CARD_HEIGHT_PX - icon_h - 12
|
|
||||||
draw.text((icon_x, icon_y), icon, font=font_icon, fill=0)
|
|
||||||
else:
|
|
||||||
font_error_msgs.append("[!] Icon font missing")
|
|
||||||
|
|
||||||
# If any font errors, show a warning on the card
|
|
||||||
if font_error_msgs:
|
|
||||||
draw.text((10, CARD_HEIGHT_PX-24), ", ".join(font_error_msgs), font=ImageFont.load_default(), fill=128)
|
|
||||||
|
|
||||||
img_path = OUT_DIR / f"task_{task.id}.png"
|
|
||||||
img.save(img_path)
|
|
||||||
preview_img = str(img_path)
|
|
||||||
msg = "success"
|
|
||||||
task.status = "printed"
|
|
||||||
else:
|
else:
|
||||||
printer = escpos.printer.Serial(devfile="/dev/ttyUSB0", baudrate=19200, timeout=1)
|
content_lines = textwrap.wrap(task.content, width=24)
|
||||||
|
|
||||||
|
# Center lines vertically
|
||||||
|
|
||||||
|
# Calculate text heights using getbbox
|
||||||
|
def get_text_height(font, text):
|
||||||
|
try:
|
||||||
|
bbox = font.getbbox(text)
|
||||||
|
return bbox[3] - bbox[1]
|
||||||
|
except Exception:
|
||||||
|
return 24
|
||||||
|
|
||||||
|
line_heights = [get_text_height(font_title, line) for line in content_lines]
|
||||||
|
total_text_height = sum(line_heights) + (len(content_lines)-1)*2
|
||||||
|
y = (CARD_HEIGHT_PX - total_text_height) // 2 - 10
|
||||||
|
for idx, line in enumerate(content_lines):
|
||||||
|
try:
|
||||||
|
w = font_title.getlength(line)
|
||||||
|
except Exception:
|
||||||
|
w = len(line) * 18
|
||||||
|
draw.text(((CARD_WIDTH_PX-w)//2, y), line, font=font_title, fill=0)
|
||||||
|
y += line_heights[idx] + 2
|
||||||
|
|
||||||
|
# User (centered below text)
|
||||||
|
user_label = f"Von: {task.user}"
|
||||||
|
try:
|
||||||
|
w = font_label_f.getlength(user_label)
|
||||||
|
user_h = get_text_height(font_label_f, user_label)
|
||||||
|
except Exception:
|
||||||
|
w = len(user_label) * 10
|
||||||
|
user_h = 18
|
||||||
|
draw.text(((CARD_WIDTH_PX-w)//2, y+8), user_label, font=font_label_f, fill=0)
|
||||||
|
y += user_h + 12
|
||||||
|
|
||||||
|
# Priority (centered below user)
|
||||||
|
prio_label = f"Priorität: {task.priority}"
|
||||||
|
try:
|
||||||
|
w = font_label_f.getlength(prio_label)
|
||||||
|
except Exception:
|
||||||
|
w = len(prio_label) * 10
|
||||||
|
draw.text(((CARD_WIDTH_PX-w)//2, y), prio_label, font=font_label_f, fill=0)
|
||||||
|
|
||||||
|
# Icon in lower right corner
|
||||||
|
icon = get_icon_for_task(task.content)
|
||||||
|
if font_icon:
|
||||||
|
icon_bbox = font_icon.getbbox(icon)
|
||||||
|
icon_w = icon_bbox[2] - icon_bbox[0]
|
||||||
|
icon_h = icon_bbox[3] - icon_bbox[1]
|
||||||
|
icon_x = CARD_WIDTH_PX - icon_w - 16
|
||||||
|
icon_y = CARD_HEIGHT_PX - icon_h - 12
|
||||||
|
draw.text((icon_x, icon_y), icon, font=font_icon, fill=0)
|
||||||
|
else:
|
||||||
|
font_error_msgs.append("[!] Icon font missing")
|
||||||
|
|
||||||
|
# If any font errors, show a warning on the card
|
||||||
|
if font_error_msgs:
|
||||||
|
draw.text((10, CARD_HEIGHT_PX-24), ", ".join(font_error_msgs), font=ImageFont.load_default(), fill=128)
|
||||||
|
|
||||||
|
# Save preview image
|
||||||
|
img_path = OUT_DIR / f"task_{task.id}.png"
|
||||||
|
img.save(img_path)
|
||||||
|
preview_img = str(img_path)
|
||||||
|
|
||||||
|
# Print to physical printer if enabled
|
||||||
|
if ENABLE_PHYSICAL_PRINTER:
|
||||||
|
printer = escpos.printer.Serial(devfile="/dev/serial0", baudrate=19200, timeout=1)
|
||||||
printer.text(f"Task: {task.content}\nVon: {task.user}\nPriorität: {task.priority}\n")
|
printer.text(f"Task: {task.content}\nVon: {task.user}\nPriorität: {task.priority}\n")
|
||||||
printer.cut()
|
printer.cut()
|
||||||
task.status = "printed"
|
|
||||||
msg = "success"
|
task.status = "printed"
|
||||||
|
msg = "success"
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Printer error: {e}")
|
print(f"Printer error: {e}")
|
||||||
msg = f"error:{e}"
|
msg = f"error:{e}"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue