from PIL import Image, ImageDraw, ImageFont config = { "ids": ["falling_plain"], "name": "Falling Plain", "panels": ["color", "font", "size", "position"], "default_font": "vazir" } 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' # رنگ متن پایه (خاموش) custom_text = style_config.styleColors.get('falling_plain', '#FFFFFF') base_text_color = color_parser(custom_text, (255, 255, 255, 255)) # رنگ کلمه فعال (روشن) active_hex = style_config.styleActiveColors.get('falling_plain', '#FFEB3B') active_text_color = color_parser(active_hex, (255, 235, 59, 255)) line_height_px = int(style_config.fontSize * 1.5) total_block_height = len(lines) * line_height_px bottom_reference = height - style_config.marginV start_y_of_block = bottom_reference - total_block_height space_w = draw.textlength(" ", font=font) try: ref_bbox = draw.textbbox((0, 0), "سلام", font=font, direction=getattr(style_config, 'text_direction', 'rtl'), language=getattr(style_config, 'lang_code', 'fa')) standard_text_visual_center_y = (ref_bbox[1] + ref_bbox[3]) / 2.0 standard_text_offset_y = ref_bbox[1] standard_text_h = ref_bbox[3] - ref_bbox[1] except: standard_text_visual_center_y = style_config.fontSize / 2.0 standard_text_offset_y = 0 standard_text_h = style_config.fontSize words_to_draw = [] global_word_counter = 0 temp_y = start_y_of_block # محاسبه موقعیت هندسی تک‌تک کلمات در تصویر for line_idx, metrics in enumerate(line_metrics): line_start_idx = sum(len(m["words"]) for m in line_metrics[:line_idx]) cursor_x = (width + metrics["width"]) / 2 + (style_config.x or 0) if is_rtl else (width - metrics["width"]) / 2 + (style_config.x or 0) line_center_y = temp_y + (line_height_px / 2.0) for w_i, word in enumerate(metrics["words"]): g_idx = line_start_idx + w_i w_len = metrics["word_widths"][w_i] text_y_pos = line_center_y - standard_text_visual_center_y word_x = (cursor_x - w_len) if is_rtl else cursor_x words_to_draw.append({ "text": word, "x": word_x, "y": text_y_pos, "width": w_len, "global_idx": global_word_counter }) cursor_x += (-(w_len + space_w) if is_rtl else (w_len + space_w)) global_word_counter += 1 temp_y += line_height_px VERTICAL_CORRECTION = int(style_config.fontSize * 0.1) current_render_time = getattr(style_config, 'current_render_time', 0.0) # رسم کلمات با انیمیشن فرود از ارتفاع -45px for item in words_to_draw: should_draw = False y_offset = 0 alpha_factor = 1.0 is_active = False if word_infos and item["global_idx"] < len(word_infos): w_info = word_infos[item["global_idx"]] if current_render_time >= w_info.start: should_draw = True is_active = current_render_time < w_info.end # اعمال انیمیشن سقوط برای کلمه فعال if is_active: time_passed = current_render_time - w_info.start anim_dur = 0.25 if time_passed < anim_dur: progress = time_passed / anim_dur ease = 1 - (1 - progress) * (1 - progress) y_offset = -45 * (1 - ease) alpha_factor = ease else: y_offset = 0 alpha_factor = 1.0 else: y_offset = 0 alpha_factor = 1.0 else: # کلمات جلو (آینده) به هیچ عنوان ترسیم نمی‌شوند should_draw = False elif active_idx != -1: if item["global_idx"] <= active_idx: should_draw = True is_active = (item["global_idx"] == active_idx) else: # کلمات آینده در حالت بدون فریم‌به‌فریم هم ترسیم نمی‌شوند should_draw = False if should_draw: # اعمال کم‌رنگ شدن کلمات قبل (گذشته) در صورت فعال بودن دکمه if not is_active and word_infos and item["global_idx"] < len(word_infos): w_info = word_infos[item["global_idx"]] if current_render_time >= w_info.end and getattr(style_config, 'fadeSurrounding', False): alpha_factor = 0.35 fill_color = active_text_color if (is_active and getattr(style_config, 'useActiveColor', True)) else base_text_color if word_infos and item["global_idx"] < len(word_infos): w_obj = word_infos[item["global_idx"]] if hasattr(w_obj, 'color') and w_obj.color: fill_color = color_parser(w_obj.color, fill_color) final_alpha = int(fill_color[3] * alpha_factor) final_color = (fill_color[0], fill_color[1], fill_color[2], final_alpha) final_text_y = item["y"] + VERTICAL_CORRECTION draw.text((item["x"], final_text_y + y_offset), item["text"], font=font, fill=final_color, direction=getattr(style_config, 'text_direction', 'rtl'), language=getattr(style_config, 'lang_code', 'fa'))