File size: 5,936 Bytes
133eb3e
c0ebdc4
 
940ffe6
c0ebdc4
 
 
133eb3e
 
 
 
 
c0ebdc4
 
 
 
133eb3e
 
 
 
 
c0ebdc4
 
 
 
940ffe6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c0ebdc4
940ffe6
 
 
 
133eb3e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c0ebdc4
 
 
133eb3e
c0ebdc4
133eb3e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c0ebdc4
133eb3e
 
c0ebdc4
133eb3e
 
 
 
c0ebdc4
133eb3e
c0ebdc4
133eb3e
 
 
c0ebdc4
133eb3e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c0ebdc4
133eb3e
 
 
 
c0ebdc4
133eb3e
 
 
 
 
 
c0ebdc4
133eb3e
 
c0ebdc4
133eb3e
c0ebdc4
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# utils/generators/base.py
import io
import os
import urllib.request
from PIL import Image, ImageDraw, ImageFont

class BaseGenerator:
    CANVAS_W, CANVAS_H = (660, 900)  
    CARD_W, CARD_H = (580, 820)
    CORNER_RAD = 16
    BANNER_H = 160                   
    OFFSET_X, OFFSET_Y = (40, 40)

    ELEMENT_COLORS = {
        "Solstice": (239, 68, 68), "Equinox": (59, 130, 246), "Eclipse": (147, 197, 253),   
        "Zenith": (168, 85, 247), "Aether": (16, 185, 129), "Nadir": (250, 204, 21),      
        "Genesis": (34, 197, 94), "Light": (255, 255, 224), "Dark": (30, 27, 75), "None": (156, 163, 175),
        "Pyro": (239, 68, 68), "Fire": (239, 68, 68), "Hydro": (59, 130, 246), "Water": (59, 130, 246),
        "Cryo": (147, 197, 253), "Ice": (147, 197, 253), "Electro": (168, 85, 247), 
        "Anemo": (16, 185, 129), "Wind": (16, 185, 129), "Geo": (250, 204, 21), "Earth": (250, 204, 21),
        "Dendro": (34, 197, 94)
    }

    @staticmethod
    def get_font(size: int):
        # 🛡️ THE BULLETPROOF FONT FIX
        base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
        assets_dir = os.path.join(base_dir, "assets")
        os.makedirs(assets_dir, exist_ok=True)
        font_path = os.path.join(assets_dir, "font.ttf")
        
        # If font is missing OR corrupted (< 10KB), download a fresh one automatically
        if not os.path.exists(font_path) or os.path.getsize(font_path) < 10000:
            try:
                print("Font missing or corrupted! Downloading fresh font...")
                url = "https://github.com/google/fonts/raw/main/ofl/roboto/Roboto-Bold.ttf"
                urllib.request.urlretrieve(url, font_path)
            except Exception as e:
                print(f"Download failed: {e}")

        try:
            return ImageFont.truetype(font_path, size)
        except Exception as e:
            print(f"Pillow failed to load font: {e}")
            return ImageFont.load_default()

    @staticmethod
    def create_rounded_mask(size, radius):
        scale = 3
        mask = Image.new('L', (size[0]*scale, size[1]*scale), 0)
        draw = ImageDraw.Draw(mask)
        draw.rounded_rectangle((0, 0, size[0]*scale, size[1]*scale), radius=radius*scale, fill=255)
        return mask.resize(size, Image.Resampling.LANCZOS)

    @staticmethod
    def create_bottom_rounded_mask(size, radius):
        scale = 3
        mask = Image.new('L', (size[0]*scale, size[1]*scale), 0)
        draw = ImageDraw.Draw(mask)
        draw.rounded_rectangle((0, 0, size[0]*scale, size[1]*scale), radius=radius*scale, fill=255)
        draw.rectangle((0, 0, size[0]*scale, radius*scale), fill=255)
        return mask.resize(size, Image.Resampling.LANCZOS)

    @staticmethod
    def draw_text_with_glow(uid, position, text, font, core_color="white", glow_color=(0,0,0)):
        uid.text(position, text, font=font, fill=core_color, stroke_width=3, stroke_fill=glow_color)

    def apply_text(self, blank_template: Image.Image, char_name: str, print_num: int, element: str, rarity: str) -> io.BytesIO:
        canvas = blank_template.copy()
        uid = ImageDraw.Draw(canvas)

        r_color = self.ELEMENT_COLORS.get(element, (250, 204, 21) if rarity in ["Legendary", "Mythic", "SSR", "UR"] else (156, 163, 175))
        light_r = tuple(min(255, c + 150) for c in r_color)
        glow_solid = r_color[:3]

        base_sz = int(self.CARD_W * 0.085)
        num_font = self.get_font(base_sz)
        num_text = str(print_num)

        # ==========================================
        # 1. Standard Number Box (100% UNTOUCHED LOGIC)
        # ==========================================
        try: 
            num_w = num_font.getlength(num_text)
        except: 
            num_w = 20
            
        box_w = num_w + 24
        box_h = 46

        box_x2 = self.OFFSET_X + self.CARD_W - 30
        box_x1 = box_x2 - box_w
        box_y1 = self.OFFSET_Y + self.CARD_H - self.BANNER_H + (self.BANNER_H - box_h) // 2
        box_y2 = box_y1 + box_h
        
        uid.rounded_rectangle((box_x1, box_y1, box_x2, box_y2), radius=10, fill=(10, 10, 10, 180), outline=light_r, width=2)

        num_x = box_x1 + (box_w - num_w) / 2
        num_y = box_y1 - 6
        self.draw_text_with_glow(uid, (num_x, num_y), num_text, num_font, core_color="white", glow_color=glow_solid)

        # ==========================================
        # 2. Dynamic Centered Character Name 
        # ==========================================
        final_char_name = char_name.title()
        available_name_w = (box_x1 - self.OFFSET_X) - 20 
        
        name_sz = base_sz
        min_sz = 20
        name_font = self.get_font(name_sz)
        
        # ⚡ THE FIX: Dynamic Scaler Loop. Shrink font until it fits the available width.
        while True:
            try:
                n_w = name_font.getlength(final_char_name)
            except AttributeError:
                n_w = 20
            if n_w <= available_name_w or name_sz <= min_sz:
                break
            name_sz -= 2
            name_font = self.get_font(name_sz)

        try: 
            n_bbox = uid.textbbox((0, 0), final_char_name, font=name_font, stroke_width=3)
        except: 
            n_bbox = (0, 0, n_w, name_sz)

        n_w_final = n_bbox[2] - n_bbox[0]
        n_h_final = n_bbox[3] - n_bbox[1]
        
        # Center horizontally in the available space, and perfectly vertically in the banner
        name_x = self.OFFSET_X + 10 + (available_name_w - n_w_final) // 2 - n_bbox[0]
        name_y = self.OFFSET_Y + self.CARD_H - self.BANNER_H + (self.BANNER_H - n_h_final) // 2 - n_bbox[1]

        self.draw_text_with_glow(uid, (name_x, name_y), final_char_name, name_font, core_color="white", glow_color=glow_solid)
        
        buf = io.BytesIO()
        canvas.save(buf, format="WEBP", quality=85, method=0) 
        buf.seek(0)
        return buf