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