File size: 5,252 Bytes
732ca45
 
 
 
 
 
865cb1a
 
 
 
 
732ca45
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
865cb1a
732ca45
 
 
 
 
 
 
 
 
 
 
 
 
865cb1a
 
 
732ca45
 
 
865cb1a
732ca45
865cb1a
 
732ca45
865cb1a
732ca45
 
 
 
865cb1a
732ca45
 
865cb1a
732ca45
 
865cb1a
 
 
732ca45
865cb1a
732ca45
 
865cb1a
732ca45
 
865cb1a
732ca45
 
 
 
865cb1a
732ca45
 
865cb1a
732ca45
 
 
865cb1a
732ca45
 
 
 
 
 
865cb1a
 
732ca45
 
 
 
 
 
 
 
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
# utils/generators/base.py
import io
import os
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):
        base_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
        font_path = os.path.join(base_dir, "assets", "font.ttf")
        try:
            if os.path.exists(font_path): return ImageFont.truetype(font_path, size)
        except: pass
        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