from PIL import Image, ImageDraw, ImageFont, ImageFilter config = { "ids": ["blue_cloud"], "name": "Blue Cloud", "panels": ["font", "size", "position"], "default_font": "lalezar" } frontend_template = "" def draw_frame(draw, img, width, height, style_config, lines, line_metrics, active_idx, font, color_parser, word_infos=None): is_rtl = getattr(style_config, 'text_direction', 'rtl') != 'ltr' # --- محاسبات جایگاه متن --- line_height_px = int(style_config.fontSize * 1.5) total_text_height = len(lines) * line_height_px center_x = width / 2 + (style_config.x or 0) bottom_margin = style_config.marginV start_y = height - bottom_margin - total_text_height vertical_correction = int(style_config.fontSize * 0.1) current_text_y = start_y - vertical_correction # --- تنظیمات رنگ دقیق (داینامیک شده) --- # دریافت رنگ متن از پنل کاربر (پیش‌فرض آبی روشن اگر چیزی انتخاب نکرده باشد) custom_hex = style_config.styleColors.get('blue_cloud', '#00b4ff') text_fill = color_parser(custom_hex, (0, 180, 255, 255)) stroke_color = (255, 255, 255, 255) # سفید خالص # --- اصلاح ضخامت‌ها و فاصله‌ها --- # ضخامت حاشیه سفید (کاهش یافته برای ظرافت) stroke_w = max(2, int(style_config.fontSize * 0.072)) # ضخامت هاله cloud_stroke_w = max(5, int(style_config.fontSize * 0.275)) cloud_blur = 5 # استانداردسازی دقیق فاصله کلمات (دقیقاً مشابه استایل‌های ساده) space_w = draw.textlength(" ", font=font) actual_space_w = space_w # --- مرحله ۱: جمع‌آوری مختصات دقیق تمام کلمات --- words_to_draw = [] global_word_counter = 0 for line_idx, metrics in enumerate(line_metrics): # عرض خط دقیقاً همان عرض محاسبه شده پیش‌فرض است بدون هیچ فاصله اضافی total_extra_spacing = 0 adjusted_line_width = metrics["width"] + total_extra_spacing cursor_x = (center_x + (adjusted_line_width / 2)) if is_rtl else (center_x - (adjusted_line_width / 2)) for w_i, word in enumerate(metrics["words"]): w_len = metrics["word_widths"][w_i] word_x = int(cursor_x - w_len) if is_rtl else int(cursor_x) word_y = int(current_text_y) # تعیین رنگ کلمه فعال (پیش‌فرض همان رنگ متن) is_active = (global_word_counter == active_idx) and getattr(style_config, 'useActiveColor', True) active_hex = style_config.styleActiveColors.get('blue_cloud', custom_hex) current_word_color = color_parser(active_hex, text_fill) if is_active else text_fill # اگر کاربر از ابزار رنگ کلمات برای رنگ کردن یک کلمه خاص استفاده کرده باشد، اینجا اعمال می‌شود if word_infos and global_word_counter < len(word_infos): w_obj = word_infos[global_word_counter] if hasattr(w_obj, 'color') and w_obj.color: current_word_color = color_parser(w_obj.color, current_word_color) # --- منطق انیمیشن تایپ --- display_word = word draw_x = word_x should_draw = True is_typewriter = getattr(style_config, 'typewriter', False) if is_typewriter and word_infos and global_word_counter < len(word_infos): w_info = word_infos[global_word_counter] current_t = getattr(style_config, 'current_render_time', 0.0) if current_t < w_info.start: should_draw = False elif current_t >= w_info.start and current_t < w_info.end: char_len = len(word) progress = (current_t - w_info.start) / max(0.001, w_info.end - w_info.start) visible_chars = min(char_len, int(progress * char_len) + 1) display_word = word[:visible_chars] try: sub_w = draw.textlength(display_word, font=font, direction=getattr(style_config, 'text_direction', 'rtl'), language=getattr(style_config, 'lang_code', 'fa')) except: sub_w = font.getlength(display_word) draw_x = int(cursor_x - sub_w) if is_rtl else draw_x if should_draw and display_word: words_to_draw.append({ "text": display_word, "x": draw_x, "y": word_y, "color": current_word_color }) cursor_x += (-(w_len + actual_space_w) if is_rtl else (w_len + actual_space_w)) global_word_counter += 1 current_text_y += line_height_px # --- مرحله ۲: رسم هاله (Cloud) با رنگ داینامیک و تراز هوشمند --- glow_layer = Image.new('RGBA', (width, height), (0, 0, 0, 0)) g_draw = ImageDraw.Draw(glow_layer) # اصلاح تراز عمودی هاله (ابر) اختصاصاً برای فونت‌های خاص glow_y_offset = 0 if style_config.font == 'pinar': glow_y_offset = -int(cloud_stroke_w * 0.6) # هدایت به سمت بالا elif style_config.font in ['dastnevis', 'entazar', 'kamran']: glow_y_offset = int(cloud_stroke_w * 0.6) # هدایت به سمت پایین for item in words_to_draw: # استخراج رنگ خودِ این کلمه و اختصاص آن به ابرش (با کمی شفافیت: 230) r, g, b, _ = item["color"] current_cloud_color = (r, g, b, 230) g_draw.text((item["x"], item["y"] + glow_y_offset), item["text"], font=font, fill=current_cloud_color, stroke_width=cloud_stroke_w, stroke_fill=current_cloud_color, direction=getattr(style_config, 'text_direction', 'rtl'), language=getattr(style_config, 'lang_code', 'fa')) # محو کردن کل لایه هاله glow_layer = glow_layer.filter(ImageFilter.GaussianBlur(cloud_blur)) # چسباندن لایه هاله در پس‌زمینه img.paste(glow_layer, (0, 0), glow_layer) # --- مرحله ۳: رسم حاشیه سفید --- for item in words_to_draw: draw.text((item["x"], item["y"]), item["text"], font=font, fill=stroke_color, stroke_width=stroke_w, stroke_fill=stroke_color, direction=getattr(style_config, 'text_direction', 'rtl'), language=getattr(style_config, 'lang_code', 'fa')) # --- مرحله ۴: رسم رنگ اصلی متن روی لایه‌های قبلی --- for item in words_to_draw: draw.text((item["x"], item["y"]), item["text"], font=font, fill=item["color"], direction=getattr(style_config, 'text_direction', 'rtl'), language=getattr(style_config, 'lang_code', 'fa'))