| |
| import io |
| from PIL import Image, ImageOps, ImageFilter, ImageDraw |
| from .base import BaseGenerator |
| from .legendary import PremiumGenerator |
| from .common import CommonGenerator |
| from .custom import CustomGenerator |
|
|
| class ProfileGenerator(BaseGenerator): |
| def build_profile(self, username: str, stats: dict, showcase_bytes: bytes = None, team_data: list = None, avatar_bytes: bytes = None) -> io.BytesIO: |
| |
| card_w, card_h = 580, 820 |
| gap = 10 |
| margin = 15 |
| columns, rows = 4, 2 |
| |
| padding = 40 |
|
|
| W = (margin * 2) + (card_w * columns) + (gap * (columns - 1)) |
| H = (margin * 2) + (card_h * rows) + (gap * (rows - 1)) |
|
|
| canvas = Image.new("RGBA", (W, H), (0, 0, 0, 255)) |
| cleanup = [canvas] |
|
|
| try: |
| shadow = Image.new("RGBA", (card_w + 20, card_h + 20), (0, 0, 0, 0)) |
| shadow_draw = ImageDraw.Draw(shadow) |
| shadow_draw.rectangle([4, 4, card_w + 16, card_h + 16], fill=(0, 0, 0, 180)) |
| pre_rendered_shadow = shadow.filter(ImageFilter.GaussianBlur(12)) |
| cleanup.append(pre_rendered_shadow) |
|
|
| if team_data: |
| |
| active_cards = [card for card in team_data if card and card.get('bytes')] |
| |
| for i, t_char in enumerate(active_cards): |
| if i >= 8: break |
|
|
| col = i % columns |
| row = i // columns |
|
|
| inner_x = margin + col * (card_w + gap) |
| inner_y = margin + row * (card_h + gap) |
|
|
| try: |
| if t_char.get('custom_frame_url') and t_char.get('frame_bytes'): |
| generator = CustomGenerator() |
| full_card = generator.build_template(t_char['bytes'], t_char['frame_bytes'], t_char['name']) |
| elif t_char.get('rarity') in ["SSR", "UR"]: |
| generator = PremiumGenerator() |
| full_card = generator.build_template(t_char['bytes'], t_char.get('element', 'None')) |
| else: |
| generator = CommonGenerator() |
| full_card = generator.build_template(t_char['bytes'], t_char.get('element', 'None')) |
|
|
| cleanup.append(full_card) |
|
|
| text_io = generator.apply_text( |
| full_card, |
| t_char.get('name', 'Unknown'), |
| t_char.get('print_num', 1), |
| t_char.get('element', 'None'), |
| t_char.get('rarity', 'C') |
| ) |
| cleanup.append(text_io) |
|
|
| finished_card = Image.open(text_io).convert("RGBA") |
| cleanup.append(finished_card) |
|
|
| if finished_card.size == (660, 900): |
| cropped_card = finished_card.crop(( |
| padding, |
| padding, |
| finished_card.width - padding, |
| finished_card.height - padding |
| )) |
| else: |
| cropped_card = finished_card |
| |
| cleanup.append(cropped_card) |
|
|
| if cropped_card.size != (card_w, card_h): |
| cropped_card = cropped_card.resize((card_w, card_h), Image.Resampling.LANCZOS) |
| cleanup.append(cropped_card) |
|
|
| canvas.paste(pre_rendered_shadow, (inner_x - 6, inner_y - 6), pre_rendered_shadow) |
| canvas.paste(cropped_card, (inner_x, inner_y), cropped_card) |
|
|
| except Exception as e: |
| print(f"Failed to build card {i}: {e}") |
|
|
| buf = io.BytesIO() |
| canvas.save(buf, format="WEBP", quality=95) |
| buf.seek(0) |
| return buf |
|
|
| finally: |
| for img in cleanup: |
| try: |
| img.close() |
| except: |
| pass |
|
|