한국 남아공 경기 공유 썸네일에 투표 픽 스코어 카드 생성
parent
39766bae55
commit
198f20a0f6
|
|
@ -22,8 +22,19 @@ from ..schedule_data import TEAMS
|
||||||
router = APIRouter(prefix="/api/og", tags=["og"])
|
router = APIRouter(prefix="/api/og", tags=["og"])
|
||||||
|
|
||||||
_KOR_NAME = {code: t["shortName"] for code, t in TEAMS.items()}
|
_KOR_NAME = {code: t["shortName"] for code, t in TEAMS.items()}
|
||||||
|
# fonts-nanum 에 항상 포함되는 Bold 만 사용(ExtraBold 는 패키지에 없을 수 있어 폴백→깨짐).
|
||||||
_FONT_BOLD = "/usr/share/fonts/truetype/nanum/NanumGothicBold.ttf"
|
_FONT_BOLD = "/usr/share/fonts/truetype/nanum/NanumGothicBold.ttf"
|
||||||
_FONT_EBOLD = "/usr/share/fonts/truetype/nanum/NanumGothicExtraBold.ttf"
|
_FONT_REG = "/usr/share/fonts/truetype/nanum/NanumGothic.ttf"
|
||||||
|
|
||||||
|
|
||||||
|
def _font_any(size: int) -> "ImageFont.FreeTypeFont":
|
||||||
|
"""Bold → Regular → 기본 순으로 시도(경로 누락 시에도 한글 유지)."""
|
||||||
|
for path in (_FONT_BOLD, _FONT_REG):
|
||||||
|
try:
|
||||||
|
return ImageFont.truetype(path, size)
|
||||||
|
except Exception: # noqa: BLE001
|
||||||
|
continue
|
||||||
|
return ImageFont.load_default()
|
||||||
|
|
||||||
W, H = 1200, 630
|
W, H = 1200, 630
|
||||||
BG = (14, 17, 22)
|
BG = (14, 17, 22)
|
||||||
|
|
@ -32,13 +43,6 @@ GREEN = (61, 224, 138)
|
||||||
SUB = (150, 160, 172)
|
SUB = (150, 160, 172)
|
||||||
|
|
||||||
|
|
||||||
def _font(path: str, size: int) -> ImageFont.FreeTypeFont:
|
|
||||||
try:
|
|
||||||
return ImageFont.truetype(path, size)
|
|
||||||
except Exception: # noqa: BLE001
|
|
||||||
return ImageFont.load_default()
|
|
||||||
|
|
||||||
|
|
||||||
def _label(code: str | None) -> str:
|
def _label(code: str | None) -> str:
|
||||||
if not code:
|
if not code:
|
||||||
return ""
|
return ""
|
||||||
|
|
@ -75,40 +79,29 @@ async def og_match_png(
|
||||||
|
|
||||||
img = Image.new("RGB", (W, H), BG)
|
img = Image.new("RGB", (W, H), BG)
|
||||||
d = ImageDraw.Draw(img)
|
d = ImageDraw.Draw(img)
|
||||||
# 상단 그린 악센트 바
|
|
||||||
d.rectangle([0, 0, W, 8], fill=GREEN)
|
|
||||||
|
|
||||||
f_top = _font(_FONT_BOLD, 44)
|
f_team = _font_any(120)
|
||||||
f_team = _font(_FONT_EBOLD, 96)
|
f_score = _font_any(134)
|
||||||
f_score = _font(_FONT_EBOLD, 110)
|
|
||||||
f_foot = _font(_FONT_BOLD, 34)
|
|
||||||
|
|
||||||
def text_w(txt: str, font: ImageFont.FreeTypeFont) -> int:
|
def text_w(txt: str, font: ImageFont.FreeTypeFont) -> int:
|
||||||
box = d.textbbox((0, 0), txt, font=font)
|
box = d.textbbox((0, 0), txt, font=font)
|
||||||
return box[2] - box[0]
|
return box[2] - box[0]
|
||||||
|
|
||||||
def center(txt: str, font: ImageFont.FreeTypeFont, y: int, fill) -> None:
|
# 중앙에 결과만: 한국 2 : 0 남아공 (스코어 그린) — 가로·세로 정중앙
|
||||||
d.text(((W - text_w(txt, font)) / 2, y), txt, font=font, fill=fill)
|
gap = 50
|
||||||
|
|
||||||
# 상단 라벨
|
|
||||||
center("TriplePick · 내 예측", f_top, 78, SUB)
|
|
||||||
|
|
||||||
# 중앙: 한국 2 : 0 남아공 (스코어 그린)
|
|
||||||
gap = 44
|
|
||||||
wa, ws, wb = text_w(la, f_team), text_w(score_txt, f_score), text_w(lb, f_team)
|
wa, ws, wb = text_w(la, f_team), text_w(score_txt, f_score), text_w(lb, f_team)
|
||||||
total = wa + gap + ws + gap + wb
|
total = wa + gap + ws + gap + wb
|
||||||
x = (W - total) / 2
|
x = (W - total) / 2
|
||||||
y_team = 250
|
score_h = d.textbbox((0, 0), score_txt, font=f_score)[3]
|
||||||
y_score = y_team - 8 # 스코어가 더 커서 베이스라인 대략 맞춤
|
team_h = d.textbbox((0, 0), la or "가", font=f_team)[3]
|
||||||
|
y_score = (H - score_h) // 2
|
||||||
|
y_team = (H - team_h) // 2
|
||||||
d.text((x, y_team), la, font=f_team, fill=WHITE)
|
d.text((x, y_team), la, font=f_team, fill=WHITE)
|
||||||
x += wa + gap
|
x += wa + gap
|
||||||
d.text((x, y_score), score_txt, font=f_score, fill=GREEN)
|
d.text((x, y_score), score_txt, font=f_score, fill=GREEN)
|
||||||
x += ws + gap
|
x += ws + gap
|
||||||
d.text((x, y_team), lb, font=f_team, fill=WHITE)
|
d.text((x, y_team), lb, font=f_team, fill=WHITE)
|
||||||
|
|
||||||
# 하단 도메인
|
|
||||||
center("triplepick.o2o.kr", f_foot, H - 78, SUB)
|
|
||||||
|
|
||||||
buf = io.BytesIO()
|
buf = io.BytesIO()
|
||||||
img.save(buf, format="PNG")
|
img.save(buf, format="PNG")
|
||||||
return Response(
|
return Response(
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue