Files
Victor Alexandrovich Tsyrenschikov 373ed28445 python
2026-03-30 20:25:42 +05:00

100 lines
2.8 KiB
Python

import io
import os
import re
from PIL import Image, ImageDraw, ImageFont
WIDTH = 1280
HEIGHT = 720
WIDTH2 = WIDTH // 2
HEIGHT2 = HEIGHT // 2
HEIGHT6 = HEIGHT // 6
def font_path() -> str:
dirname = os.path.dirname(os.path.realpath(__file__))
return os.path.join(dirname, "fonts", "DejaVuSans.ttf")
def draw_text(
ctx: ImageDraw,
text: str,
box: tuple,
anchor: str,
fill: str | tuple,
font_size: int,
line_width: int = 20,
):
"""Draw multiline text inside box with smart anchor."""
lines = re.findall(r"(.{1,%d})(?:\s|$)" % line_width, text)
if (font_size > 70 and len(lines) > 3) or (font_size <= 70 and len(lines) > 4):
return draw_text(ctx, text, box, anchor, fill, font_size - 10, line_width + 3)
# https://pillow.readthedocs.io/en/stable/handbook/text-anchors.html#text-anchors
if anchor[0] == "l":
x = box[0]
align = "la" # left-ascender
elif anchor[0] == "m":
x = box[0] + (box[2]) // 2
align = "ma" # middle-ascender
elif anchor[0] == "r":
x = box[0] + box[2]
align = "ra" # right-ascender
else:
raise NotImplementedError(anchor)
if anchor[1] == "t":
y = box[1]
elif anchor[1] == "m":
y = box[1] + (box[3] - len(lines) * font_size) // 2
elif anchor[1] == "b":
y = box[1] + (box[3] - len(lines) * font_size)
else:
raise NotImplementedError(anchor)
font = ImageFont.truetype(font_path(), font_size, encoding="UTF-8")
for line in lines:
ctx.text((x, y), line, anchor=align, fill=fill, font=font)
y += font_size
def draw_cover(title: str, artist: str, cover: bytes) -> bytes:
cover_canvas = Image.open(io.BytesIO(cover))
assert cover_canvas.size == (400, 400)
canvas = Image.new("RGB", (WIDTH, HEIGHT))
canvas.paste(cover_canvas, (WIDTH2 - 200, HEIGHT6 * 2 - 200))
ctx = ImageDraw.Draw(canvas)
if title:
draw_text(ctx, title, (0, HEIGHT6 * 4, WIDTH, HEIGHT6), "mb", "white", 60, 35)
if artist:
draw_text(ctx, artist, (0, HEIGHT6 * 5, WIDTH, HEIGHT6), "mt", "grey", 50, 40)
bytes = io.BytesIO()
canvas.save(bytes, format="JPEG", quality=75)
return bytes.getvalue()
def draw_lyrics(first: str | None, second: str | None) -> bytes:
canvas = Image.new("RGB", (WIDTH, HEIGHT))
ctx = ImageDraw.Draw(canvas)
if first:
draw_text(ctx, first, (0, 50, WIDTH, HEIGHT2 - 50), "mm", "white", 100)
if second:
draw_text(ctx, second, (0, HEIGHT2, WIDTH, HEIGHT2 - 50), "mm", "grey", 100)
bytes = io.BytesIO()
canvas.save(bytes, format="JPEG", quality=75)
return bytes.getvalue()
def draw_none() -> bytes:
canvas = Image.new("RGB", (WIDTH, HEIGHT), "grey")
bytes = io.BytesIO()
canvas.save(bytes, format="JPEG", quality=75)
return bytes.getvalue()