From 694b91b69d745389d3e9bcbd0f1fcfb920cd4ba9 Mon Sep 17 00:00:00 2001 From: Valerie Date: Sun, 25 May 2025 22:05:36 -0400 Subject: [PATCH] Refine profile card generation in default.py by adjusting layout dimensions for improved spacing, reducing font sizes, and enhancing the stats area design. Update the progress bar and XP text positioning for better visual clarity and overall aesthetics. --- levelup/generator/styles/default.py | 131 +++++++++++++++------------- 1 file changed, 68 insertions(+), 63 deletions(-) diff --git a/levelup/generator/styles/default.py b/levelup/generator/styles/default.py index 9e54412..17acb3a 100644 --- a/levelup/generator/styles/default.py +++ b/levelup/generator/styles/default.py @@ -196,15 +196,15 @@ def generate_default_profile( if square: desired_card_size = (450, 450) else: - # Reduce height for better proportions - desired_card_size = (1050, 350) + # Keep width but adjust height for better spacing + desired_card_size = (1050, 300) # Define the stats area with a modern glass effect stats_area = ( - 400, # x1 - Start after profile picture - 20, # y1 - Start near top - 1000 if not square else 430, # x2 - End near right edge - 330 if not square else 430 # y2 - End near bottom + 380, # x1 - Start after profile picture (moved left slightly) + 15, # y1 - Start near top + 1020, # x2 - End near right edge + 285 # y2 - Reduced height ) # Create the stats layer with glass effect @@ -213,7 +213,7 @@ def generate_default_profile( # Create a semi-transparent background for stats with modern blur effect glass = Image.new("RGBA", desired_card_size, (0, 0, 0, 0)) glass_draw = ImageDraw.Draw(glass) - glass_draw.rounded_rectangle(stats_area, radius=25, fill=(0, 0, 0, 95)) + glass_draw.rounded_rectangle(stats_area, radius=20, fill=(0, 0, 0, 95)) # Add a subtle gradient overlay for depth gradient = Image.new("RGBA", desired_card_size, (0, 0, 0, 0)) @@ -222,7 +222,7 @@ def generate_default_profile( opacity = int(60 * (1 - i/30)) gradient_draw.rounded_rectangle( (stats_area[0], stats_area[1]+i, stats_area[2], stats_area[3]), - radius=25, + radius=20, fill=(255, 255, 255, opacity) ) @@ -235,7 +235,7 @@ def generate_default_profile( for i in range(3): border_draw.rounded_rectangle( (stats_area[0]-i, stats_area[1]-i, stats_area[2]+i, stats_area[3]+i), - radius=25, + radius=20, outline=(155, 17, 30, 100-i*30), width=1 ) @@ -248,68 +248,92 @@ def generate_default_profile( if prestige > 0: level_text = f"P{prestige} • {level_text}" - # Use larger font for level display - level_font = ImageFont.truetype(str(font_path or imgtools.DEFAULT_FONT), 56) - level_y = stats_area[1] + 15 + # Use smaller font for level display to prevent overlap + level_font = ImageFont.truetype(str(font_path or imgtools.DEFAULT_FONT), 42) + level_y = stats_area[1] + 10 # Add subtle text shadow for depth shadow_offset = 2 draw.text( - (stats_area[0] + 20 + shadow_offset, level_y + shadow_offset), + (stats_area[0] + 15 + shadow_offset, level_y + shadow_offset), level_text, font=level_font, fill=(0, 0, 0, 100) ) draw.text( - (stats_area[0] + 20, level_y), + (stats_area[0] + 15, level_y), level_text, font=level_font, fill=user_color ) # Stats section with improved layout - title_font_size = 24 - value_font_size = 28 + title_font_size = 20 # Reduced font size + value_font_size = 24 # Reduced font size title_font = ImageFont.truetype(str(font_path or imgtools.DEFAULT_FONT), title_font_size) value_font = ImageFont.truetype(str(font_path or imgtools.DEFAULT_FONT), value_font_size) - spacing = 45 # Vertical spacing between stats + spacing = 35 # Reduced spacing between stats # Starting positions - start_x = stats_area[0] + 20 - start_y = level_y + 80 + start_x = stats_area[0] + 15 + start_y = level_y + 60 # Reduced gap after level - # Helper function for stat rendering - def draw_stat(y_pos, icon, title, value, color=stat_color): + # Helper function for stat rendering with improved spacing + def draw_stat(x_pos, y_pos, icon, title, value, color=stat_color): # Draw icon - draw.text((start_x, y_pos), icon, font=value_font, fill=color) + draw.text((x_pos, y_pos + 2), icon, font=value_font, fill=color) # Draw title - draw.text((start_x + 40, y_pos), f"{title}:", font=title_font, fill=(200, 200, 200)) + title_x = x_pos + 30 + draw.text((title_x, y_pos), f"{title}:", font=title_font, fill=(200, 200, 200)) + # Calculate value position + title_width = draw.textlength(f"{title}:", font=title_font) # Draw value with shadow for depth - value_x = start_x + 40 - value_y = y_pos + title_font_size + 2 - draw.text((value_x + 1, value_y + 1), value, font=value_font, fill=(0, 0, 0, 100)) - draw.text((value_x, value_y), value, font=value_font, fill=color) - return spacing + title_font_size + 5 + value_x = title_x + title_width + 5 + draw.text((value_x + 1, y_pos + 1), value, font=value_font, fill=(0, 0, 0, 100)) + draw.text((value_x, y_pos), value, font=value_font, fill=color) + return spacing - # Draw each stat with title and value + # Draw stats in two columns with better spacing + left_column_x = start_x + right_column_x = stats_area[0] + 300 current_y = start_y - current_y += draw_stat(current_y, "💬", "Messages", f"{humanize_number(messages)} sent") - current_y += draw_stat(current_y, "🎤", "Voice Time", imgtools.abbreviate_time(voicetime)) - current_y += draw_stat(current_y, "⭐", "Stars", humanize_number(stars)) - + + # Left column stats + current_y += draw_stat(left_column_x, current_y, "💬", "Messages", f"{humanize_number(messages)}") + current_y += draw_stat(left_column_x, current_y, "🎤", "Voice", imgtools.abbreviate_time(voicetime)) + current_y += draw_stat(left_column_x, current_y, "⭐", "Stars", humanize_number(stars)) + # Right column stats - right_x = stats_area[0] + 300 current_y = start_y if balance is not None: - current_y += draw_stat(current_y, "💰", "Balance", f"{humanize_number(balance)} {currency_name}") - current_y += draw_stat(current_y, "🏆", "Rank", f"#{humanize_number(position)}") + current_y += draw_stat(right_column_x, current_y, "💰", "Balance", f"{humanize_number(balance)} {currency_name}") + current_y += draw_stat(right_column_x, current_y, "🏆", "Rank", f"#{humanize_number(position)}") - # Progress bar with modern design - bar_width = stats_area[2] - stats_area[0] - 40 - bar_height = 30 - bar_y = stats_area[3] - 60 + # Progress bar with modern design - moved down + bar_width = stats_area[2] - stats_area[0] - 30 # Slightly narrower + bar_height = 25 # Slightly shorter + bar_y = stats_area[3] - 45 # Move up from bottom progress = (current_xp - previous_xp) / (next_xp - previous_xp) + # XP text above progress bar + xp_font = ImageFont.truetype(str(font_path or imgtools.DEFAULT_FONT), 20) # Smaller font + xp_text = f"EXP: {humanize_number(current_xp)} / {humanize_number(next_xp)}" + xp_x = start_x + (bar_width - draw.textlength(xp_text, font=xp_font)) // 2 + + # Draw XP text with shadow + draw.text( + (xp_x + 1, bar_y - 25 + 1), # Moved closer to bar + xp_text, + font=xp_font, + fill=(0, 0, 0, 100) + ) + draw.text( + (xp_x, bar_y - 25), # Moved closer to bar + xp_text, + font=xp_font, + fill=(255, 255, 255) + ) + # Create progress bar background with gradient bar_bg = Image.new("RGBA", (bar_width, bar_height), (0, 0, 0, 100)) progress_width = max(1, int(bar_width * progress)) @@ -341,26 +365,7 @@ def generate_default_profile( stats_layer.paste(bar_bg, (start_x, bar_y), bar_bg) stats_layer.paste(bar_progress_masked, (start_x, bar_y), bar_progress_masked) - # XP text with improved styling - xp_font = ImageFont.truetype(str(font_path or imgtools.DEFAULT_FONT), 24) - xp_text = f"EXP: {humanize_number(current_xp)} / {humanize_number(next_xp)}" - xp_x = start_x + (bar_width - draw.textlength(xp_text, font=xp_font)) // 2 - - # Draw XP text with shadow - draw.text( - (xp_x + 1, bar_y - 30 + 1), - xp_text, - font=xp_font, - fill=(0, 0, 0, 100) - ) - draw.text( - (xp_x, bar_y - 30), - xp_text, - font=xp_font, - fill=(255, 255, 255) - ) - - # Progress percentage + # Progress percentage below bar percent_text = f"{int(progress * 100)}%" percent_x = start_x + (bar_width - draw.textlength(percent_text, font=xp_font)) // 2 draw.text( @@ -383,15 +388,15 @@ def generate_default_profile( card = imgtools.fit_aspect_ratio(card, desired_card_size) # Round the card corners - card = imgtools.round_image_corners(card, 25) + card = imgtools.round_image_corners(card, 20) # Create circular profile picture - pfp_size = (300, 300) # Slightly smaller profile picture + pfp_size = (270, 270) # Slightly smaller profile picture pfp = pfp.resize(pfp_size, Image.Resampling.LANCZOS) pfp = imgtools.make_profile_circle(pfp) # Position profile picture on the left - pfp_x = 50 + pfp_x = 55 pfp_y = (desired_card_size[1] - pfp_size[1]) // 2 # Create a new image for final composition