Compare commits
3 Commits
c3269bcbf2
...
55d3e69320
| Author | SHA1 | Date | |
|---|---|---|---|
| 55d3e69320 | |||
| c6a4396c4c | |||
| f464edb1da |
88
app.py
88
app.py
@ -1,6 +1,7 @@
|
|||||||
import io
|
import io
|
||||||
import tempfile
|
import tempfile
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import requests
|
||||||
from flask import Flask, render_template_string, request, send_from_directory
|
from flask import Flask, render_template_string, request, send_from_directory
|
||||||
from PIL import Image, ImageDraw, ImageFont
|
from PIL import Image, ImageDraw, ImageFont
|
||||||
import re
|
import re
|
||||||
@ -286,27 +287,48 @@ HTML_FORM = '''
|
|||||||
<li><b>Encabezado chico:</b> <code>### Título</code></li>
|
<li><b>Encabezado chico:</b> <code>### Título</code></li>
|
||||||
<li><b>Lista con viñetas:</b> <code>- Elemento</code></li>
|
<li><b>Lista con viñetas:</b> <code>- Elemento</code></li>
|
||||||
<li><b>Lista numerada:</b> <code>1. Elemento</code></li>
|
<li><b>Lista numerada:</b> <code>1. Elemento</code></li>
|
||||||
|
<li><b>Imágen:</b> <code></code></li>
|
||||||
<li><b>Salto de línea:</b> Deje una línea vacía</li>
|
<li><b>Salto de línea:</b> Deje una línea vacía</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
def bleh_image_from_url(url):
|
||||||
|
resp = requests.get(url, stream=True)
|
||||||
|
resp.raise_for_status()
|
||||||
|
bleh = subprocess.Popen(
|
||||||
|
["./bleh", "-o", "-", "-mode", "1bpp", "-d", "floyd", "-"],
|
||||||
|
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
|
out, err = bleh.communicate(resp.content)
|
||||||
|
if bleh.returncode != 0:
|
||||||
|
raise RuntimeError(f"Driver failed: {err.decode()}")
|
||||||
|
img = Image.open(io.BytesIO(out)).convert("L")
|
||||||
|
# Optionally check width, pad/resize if needed
|
||||||
|
if img.width != IMAGE_WIDTH:
|
||||||
|
img = img.resize((IMAGE_WIDTH, img.height), Image.LANCZOS)
|
||||||
|
return img
|
||||||
|
|
||||||
def parse_line(line):
|
def parse_line(line):
|
||||||
if re.match(r'^\s*---\s*$', line):
|
image_match = re.match(r"^!\[(.*?)\]\((.+?)\)", line)
|
||||||
return ('hr', [])
|
if image_match:
|
||||||
header_match = re.match(r"^(#{1,3}) +(.*)", line)
|
alt = image_match.group(1)
|
||||||
if header_match:
|
url = image_match.group(2)
|
||||||
header_level = len(header_match.group(1))
|
return ('image', url, alt)
|
||||||
line = header_match.group(2)
|
if re.match(r'^\s*---\s*$', line):
|
||||||
return ('header', header_level, parse_segments(line))
|
return ('hr', [])
|
||||||
bullet_match = re.match(r"^\s*([-*\u2022]) +(.*)", line)
|
header_match = re.match(r"^(#{1,3}) +(.*)", line)
|
||||||
if bullet_match:
|
if header_match:
|
||||||
return ('bullet', parse_segments(bullet_match.group(2)))
|
header_level = len(header_match.group(1))
|
||||||
ordered_match = re.match(r"^\s*(\d+)\. +(.*)", line)
|
line = header_match.group(2)
|
||||||
if ordered_match:
|
return ('header', header_level, parse_segments(line))
|
||||||
return ('ordered', int(ordered_match.group(1)), parse_segments(ordered_match.group(2)))
|
bullet_match = re.match(r"^\s*([-*\u2022]) +(.*)", line)
|
||||||
return ('text', parse_segments(line))
|
if bullet_match:
|
||||||
|
return ('bullet', parse_segments(bullet_match.group(2)))
|
||||||
|
ordered_match = re.match(r"^\s*(\d+)\. +(.*)", line)
|
||||||
|
if ordered_match:
|
||||||
|
return ('ordered', int(ordered_match.group(1)), parse_segments(ordered_match.group(2)))
|
||||||
|
return ('text', parse_segments(line))
|
||||||
|
|
||||||
def parse_segments(line):
|
def parse_segments(line):
|
||||||
# Handle escaped asterisks: replace them with a placeholder
|
# Handle escaped asterisks: replace them with a placeholder
|
||||||
@ -401,7 +423,13 @@ def render(md):
|
|||||||
lines_out.append(('blank', []))
|
lines_out.append(('blank', []))
|
||||||
continue
|
continue
|
||||||
tag = parse_line(src_line)
|
tag = parse_line(src_line)
|
||||||
if tag[0] == 'hr':
|
if tag[0] == 'image':
|
||||||
|
try:
|
||||||
|
image = bleh_image_from_url(tag[1])
|
||||||
|
lines_out.append(('image', image))
|
||||||
|
except Exception as e:
|
||||||
|
lines_out.append(('text', [('text', f"[Imagen inválida: {e}]")], font, FONT_SIZE))
|
||||||
|
elif tag[0] == 'hr':
|
||||||
lines_out.append(('hr',))
|
lines_out.append(('hr',))
|
||||||
elif tag[0] == 'header':
|
elif tag[0] == 'header':
|
||||||
header_level = tag[1]
|
header_level = tag[1]
|
||||||
@ -443,12 +471,18 @@ def render(md):
|
|||||||
for wrapped in wrap_segments(segments, font, font_bold, font_italic, font_bolditalic, IMAGE_WIDTH):
|
for wrapped in wrap_segments(segments, font, font_bold, font_italic, font_bolditalic, IMAGE_WIDTH):
|
||||||
lines_out.append(('text', wrapped, font, FONT_SIZE))
|
lines_out.append(('text', wrapped, font, FONT_SIZE))
|
||||||
|
|
||||||
height = sum(
|
# Compute total height, including images
|
||||||
item[3] if item[0] in ('header', 'text', 'bullet', 'ordered') else
|
height = 10 # Top margin
|
||||||
10 if item[0] == 'hr' else
|
for item in lines_out:
|
||||||
FONT_SIZE
|
if item[0] in ('header', 'text', 'bullet', 'ordered'):
|
||||||
for item in lines_out
|
height += item[3]
|
||||||
) + 10
|
elif item[0] == 'hr':
|
||||||
|
height += 10
|
||||||
|
elif item[0] == 'blank':
|
||||||
|
height += FONT_SIZE
|
||||||
|
elif item[0] == 'image':
|
||||||
|
img = item[1]
|
||||||
|
height += img.height + 10 # add margin below image
|
||||||
|
|
||||||
image = Image.new("L", (IMAGE_WIDTH, height), 255)
|
image = Image.new("L", (IMAGE_WIDTH, height), 255)
|
||||||
draw = ImageDraw.Draw(image)
|
draw = ImageDraw.Draw(image)
|
||||||
@ -500,6 +534,12 @@ def render(md):
|
|||||||
draw.text((x, y), text, font=f, fill=0)
|
draw.text((x, y), text, font=f, fill=0)
|
||||||
x += f.getbbox(text)[2] - f.getbbox(text)[0]
|
x += f.getbbox(text)[2] - f.getbbox(text)[0]
|
||||||
y += sz
|
y += sz
|
||||||
|
elif item[0] == 'image':
|
||||||
|
img = item[1]
|
||||||
|
# Center image horizontally if narrower
|
||||||
|
img_x = (IMAGE_WIDTH - img.width) // 2 if img.width < IMAGE_WIDTH else 0
|
||||||
|
image.paste(img, (img_x, y))
|
||||||
|
y += img.height + 10 # vertical margin after image
|
||||||
return image
|
return image
|
||||||
|
|
||||||
@app.route("/", methods=["GET", "POST"])
|
@app.route("/", methods=["GET", "POST"])
|
||||||
@ -516,17 +556,17 @@ def index():
|
|||||||
buf.seek(0)
|
buf.seek(0)
|
||||||
import base64
|
import base64
|
||||||
img_data = base64.b64encode(buf.getvalue()).decode()
|
img_data = base64.b64encode(buf.getvalue()).decode()
|
||||||
# If print button pressed, send to catprinter-ble
|
# If print button pressed, send to driver
|
||||||
if "print" in request.form:
|
if "print" in request.form:
|
||||||
try:
|
try:
|
||||||
with tempfile.NamedTemporaryFile(suffix=".png", delete=True) as tmpfile:
|
with tempfile.NamedTemporaryFile(suffix=".png", delete=True) as tmpfile:
|
||||||
image.save(tmpfile, format="PNG")
|
image.save(tmpfile, format="PNG")
|
||||||
tmpfile.flush()
|
tmpfile.flush()
|
||||||
# Run the catprinter-ble command
|
# Run the bleh command
|
||||||
# You can set dither method here if desired
|
# You can set dither method here if desired
|
||||||
result = subprocess.run([
|
result = subprocess.run([
|
||||||
"sudo",
|
"sudo",
|
||||||
"./catprinter-ble",
|
"./bleh",
|
||||||
"-mode", "1bpp",
|
"-mode", "1bpp",
|
||||||
"-intensity", "100",
|
"-intensity", "100",
|
||||||
tmpfile.name
|
tmpfile.name
|
||||||
|
|||||||
BIN
catprinter-ble
BIN
catprinter-ble
Binary file not shown.
Loading…
Reference in New Issue
Block a user