diff --git a/app.py b/app.py
index 2d239be..1ff595f 100644
--- a/app.py
+++ b/app.py
@@ -1,6 +1,7 @@
import io
import tempfile
import subprocess
+import requests
from flask import Flask, render_template_string, request, send_from_directory
from PIL import Image, ImageDraw, ImageFont
import re
@@ -286,27 +287,48 @@ HTML_FORM = '''
Encabezado chico: ### Título
Lista con viñetas: - Elemento
Lista numerada: 1. Elemento
+ Imágen: 
Salto de línea: Deje una línea vacía
'''
+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):
- if re.match(r'^\s*---\s*$', line):
- return ('hr', [])
- header_match = re.match(r"^(#{1,3}) +(.*)", line)
- if header_match:
- header_level = len(header_match.group(1))
- line = header_match.group(2)
- return ('header', header_level, parse_segments(line))
- bullet_match = re.match(r"^\s*([-*\u2022]) +(.*)", 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))
+ image_match = re.match(r"^!\[(.*?)\]\((.+?)\)", line)
+ if image_match:
+ alt = image_match.group(1)
+ url = image_match.group(2)
+ return ('image', url, alt)
+ if re.match(r'^\s*---\s*$', line):
+ return ('hr', [])
+ header_match = re.match(r"^(#{1,3}) +(.*)", line)
+ if header_match:
+ header_level = len(header_match.group(1))
+ line = header_match.group(2)
+ return ('header', header_level, parse_segments(line))
+ bullet_match = re.match(r"^\s*([-*\u2022]) +(.*)", 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):
# Handle escaped asterisks: replace them with a placeholder
@@ -401,7 +423,13 @@ def render(md):
lines_out.append(('blank', []))
continue
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',))
elif tag[0] == 'header':
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):
lines_out.append(('text', wrapped, font, FONT_SIZE))
- height = sum(
- item[3] if item[0] in ('header', 'text', 'bullet', 'ordered') else
- 10 if item[0] == 'hr' else
- FONT_SIZE
- for item in lines_out
- ) + 10
+ # Compute total height, including images
+ height = 10 # Top margin
+ for item in lines_out:
+ if item[0] in ('header', 'text', 'bullet', 'ordered'):
+ height += item[3]
+ 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)
draw = ImageDraw.Draw(image)
@@ -500,6 +534,12 @@ def render(md):
draw.text((x, y), text, font=f, fill=0)
x += f.getbbox(text)[2] - f.getbbox(text)[0]
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
@app.route("/", methods=["GET", "POST"])