python
This commit is contained in:
99
custom_components/yandex_station/core/image.py
Normal file
99
custom_components/yandex_station/core/image.py
Normal file
@@ -0,0 +1,99 @@
|
||||
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()
|
||||
Reference in New Issue
Block a user