File size: 4,910 Bytes
0cb2113
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
from PIL import Image, ImageDraw, ImageFont

 # Function for managing longer bodies of text and breaking into a list of lines to be printed based on input arguments   
def split_text_into_lines(text, font, max_width, draw):
    blocks = text.split('\n')  
    lines = []
    for block in blocks:
        words = block.split()
        current_line = ''

        for word in words:
            # Check width with new word added
            test_line = f"{current_line} {word}".strip()
            test_width = draw.textlength(text = test_line, font=font)
            if test_width <= max_width:
                current_line = test_line
            else:
                #If the line with the new word exceeds the max width, start a new line
                lines.append(current_line)
                current_line = word
    # add the last line
        lines.append(current_line)
    return lines
# Function for calculating the height of the text at the current font setting
       

def adjust_font_size_lines_and_spacing(text, font_path, initial_font_size, max_width, area_height, image) :    
    font_size = initial_font_size
    optimal_font_size = font_size
    optimal_lines = []
    line_spacing_factor = 1.2 # multiple of font size that will get added between each line
    
    while font_size > 10: # Set minimum font size
        font = ImageFont.truetype(font_path, font_size)
        draw = ImageDraw.Draw(image)
        # Fitting text into box dimensions
        lines = split_text_into_lines(text, font, max_width, draw)
        # Calculate total height with dynamic line spacing
        single_line_height = draw.textbbox((0, 0), "Ay", font=font)[3] - draw.textbbox((0, 0), "Ay", font=font)[1]  # Height of 'Ay'
        line_spacing = int(single_line_height * line_spacing_factor) - single_line_height
        total_text_height = len(lines) * single_line_height + (len(lines) - 1) * line_spacing # Estimate total height of all lines by multiplying number of lines by font height plus number of lines -1 times line spacing

        if total_text_height <= area_height : 
            optimal_font_size = font_size
            optimal_lines = lines
            break # Exit loop font fits in contraints

        else: 
            font_size -= 1 # Reduce font by 1 to check if it fits             
        
    return optimal_font_size, optimal_lines, line_spacing     
# Function that takes in an image,text and properties for textfrom card_generator 
def render_text_with_dynamic_spacing(image, text, center_position, max_width, area_height,font_path, initial_font_size, item_key = None, description = None, quote = None):
    if item_key:
        text = write_description(item_key)

    optimal_font_size, optimal_lines, line_spacing = adjust_font_size_lines_and_spacing(
        text, font_path, initial_font_size, max_width, area_height, image)
    # create an object to draw on 
    
    font = ImageFont.truetype(font_path, optimal_font_size)
    draw = ImageDraw.Draw(image)

     # Shadow settings
    shadow_offset = (1, 1)  # X and Y offset for shadow
    shadow_color = 'grey'  # Shadow color

    # Unsure about the following line, not sure if I want y_offset to be dynamic
    y_offset = center_position[1]         
    
    if description or quote : 
        for line in optimal_lines:
            line_width = draw.textlength(text = line, font=font)
            x = center_position[0] 
            # Draw Shadow first
            shadow_position = (x + shadow_offset[0], y_offset + shadow_offset[1])
            draw.text(shadow_position, line, font=font, fill=shadow_color)
            #Draw text
            draw.text((x, y_offset), line, font=font, fill = 'black', align = "left" )
            y_offset += optimal_font_size + line_spacing  # Move to next line
        return image  

    for line in optimal_lines:
        line_width = draw.textlength(text = line, font=font)
        x = center_position[0] - (line_width / 2)
        # Draw Shadow first
        shadow_position = (x + shadow_offset[0], y_offset + shadow_offset[1])
        draw.text(shadow_position, line, font=font, fill=shadow_color)
        #Draw text
        draw.text((x, y_offset), line, font=font, fill = 'black', align = "left" )
        y_offset += optimal_font_size + line_spacing  # Move to next line
    return image   

# Function to put the description objects together, this will be the complicated bit, I think iterate through keys excluding title, type and cost
def write_description(item_key):
    skip_list = ['Name', 'Type', 'Value', 'Weight', 'Damage', 'SD Prompt', 'Quote', 'Rarity']    
    description_list = ['\n'.join(value) for key, value in item_key.items() if key not in skip_list and type(value) == list]
    description_list += [value if key not in skip_list else '' for key, value in item_key.items() if type(value) != list]
    return '\n'.join(filter(None, description_list))