diff --git a/backend/app/routers/og_image.py b/backend/app/routers/og_image.py index f8a47f1..03f66c3 100644 --- a/backend/app/routers/og_image.py +++ b/backend/app/routers/og_image.py @@ -22,8 +22,19 @@ from ..schedule_data import TEAMS router = APIRouter(prefix="/api/og", tags=["og"]) _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_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 BG = (14, 17, 22) @@ -32,13 +43,6 @@ GREEN = (61, 224, 138) 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: if not code: return "" @@ -75,40 +79,29 @@ async def og_match_png( img = Image.new("RGB", (W, H), BG) d = ImageDraw.Draw(img) - # 상단 그린 악센트 바 - d.rectangle([0, 0, W, 8], fill=GREEN) - f_top = _font(_FONT_BOLD, 44) - f_team = _font(_FONT_EBOLD, 96) - f_score = _font(_FONT_EBOLD, 110) - f_foot = _font(_FONT_BOLD, 34) + f_team = _font_any(120) + f_score = _font_any(134) def text_w(txt: str, font: ImageFont.FreeTypeFont) -> int: box = d.textbbox((0, 0), txt, font=font) return box[2] - box[0] - def center(txt: str, font: ImageFont.FreeTypeFont, y: int, fill) -> None: - d.text(((W - text_w(txt, font)) / 2, y), txt, font=font, fill=fill) - - # 상단 라벨 - center("TriplePick · 내 예측", f_top, 78, SUB) - - # 중앙: 한국 2 : 0 남아공 (스코어 그린) - gap = 44 + # 중앙에 결과만: 한국 2 : 0 남아공 (스코어 그린) — 가로·세로 정중앙 + gap = 50 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 x = (W - total) / 2 - y_team = 250 - y_score = y_team - 8 # 스코어가 더 커서 베이스라인 대략 맞춤 + score_h = d.textbbox((0, 0), score_txt, font=f_score)[3] + 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) x += wa + gap d.text((x, y_score), score_txt, font=f_score, fill=GREEN) x += ws + gap d.text((x, y_team), lb, font=f_team, fill=WHITE) - # 하단 도메인 - center("triplepick.o2o.kr", f_foot, H - 78, SUB) - buf = io.BytesIO() img.save(buf, format="PNG") return Response(