Spaces:
Paused
Paused
| 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): | |
| # --- محاسبات جایگاه متن --- | |
| 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) | |
| for w_i, word in enumerate(metrics["words"]): | |
| w_len = metrics["word_widths"][w_i] | |
| word_x = int(cursor_x - w_len) | |
| 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='rtl', language='fa') | |
| except: sub_w = font.getlength(display_word) | |
| draw_x = int(cursor_x - sub_w) | |
| 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) | |
| 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='rtl', language='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='rtl', language='fa') | |
| # --- مرحله ۴: رسم رنگ اصلی متن روی لایههای قبلی --- | |
| for item in words_to_draw: | |
| draw.text((item["x"], item["y"]), item["text"], font=font, fill=item["color"], | |
| direction='rtl', language='fa') |