from PIL import Image, ImageDraw, ImageFont config = { "ids": ["classic", "plain_white", "white_outline", "progressive_write"], "name": "Classic Styles", "panels": ["color", "font", "size", "position", "radius", "padding"], "default_font": "vazir" } def draw_frame(draw, img, width, height, style_config, lines, line_metrics, active_idx, font, color_parser, word_infos=None): style_name = style_config.name is_rtl = getattr(style_config, 'text_direction', 'rtl') != 'ltr' base_text_color = color_parser(style_config.primaryColor, (255, 255, 255, 255)) stroke_color = color_parser(style_config.outlineColor, (0, 0, 0, 255)) stroke_width = 0 if style_name == "plain_white": c_hex = style_config.styleColors.get('plain_white', '#ffffff') base_text_color = color_parser(c_hex, (255, 255, 255, 255)) elif style_name == "white_outline": c_hex = style_config.styleColors.get('white_outline', '#ffffff') base_text_color = color_parser(c_hex, (255, 255, 255, 255)) custom_stroke_hex = style_config.styleBgColors.get('white_outline_stroke', '#000000') stroke_color = color_parser(custom_stroke_hex, (0, 0, 0, 255)) stroke_width = max(2, int(style_config.fontSize / 12)) elif style_config.backType == 'outline': stroke_width = max(2, int(style_config.fontSize / 12)) 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) max_line_width = 0 for m in line_metrics: # محاسبه واقعی عرض خط با جمع زدن تک تک کلمات برای دقت صد در صد actual_w = sum(m["word_widths"]) + max(0, len(m["words"]) - 1) * space_w if actual_w > max_line_width: max_line_width = actual_w # --- رسم کادر کلی (Container) --- should_draw_box = False box_fill_color = (0,0,0,0) if style_name in ["classic", "progressive_write"] and style_config.backType in ['solid', 'transparent']: should_draw_box = True fill_color_tuple = color_parser(style_config.outlineColor, (0, 0, 0, 255)) if style_config.backType == 'transparent' and fill_color_tuple[3] == 255: fill_color_tuple = (fill_color_tuple[0], fill_color_tuple[1], fill_color_tuple[2], 160) box_fill_color = fill_color_tuple # استخراج محاسبات کادر به بیرون از شرط برای استفاده به عنوان مبنای رسم متن ratio = height / width box_center_y_adjustment = 0 if ratio > 1.6: box_center_y_adjustment = 5 elif ratio > 1.1: box_center_y_adjustment = 10 else: box_center_y_adjustment = 15 box_center_x = width / 2 + style_config.x # کاهش 16 پیکسل از عرض کادر برای جمع‌تر شدن در سمت چپ و راست box_width = max_line_width + (style_config.paddingX * 2) - 16 box_x1 = box_center_x - (box_width / 2) box_x2 = box_center_x + (box_width / 2) visual_top_correction = int(style_config.fontSize * 0.12) # نقطه مبنای رسم کادر (برای حفظ جایگاه دقیق متن) base_y1 = start_y_of_block - style_config.paddingY + box_center_y_adjustment + visual_top_correction # تنظیم دقیق ارتفاع کادر: کاهش تنها ۱۵ درصدی (برای متعادل شدن بالا و پایین کادر) box_y1 = base_y1 + (style_config.paddingY * 0.15) box_y2 = start_y_of_block + total_block_height + (style_config.paddingY * 0.85) + box_center_y_adjustment - (line_height_px * 0.3) if should_draw_box: draw.rounded_rectangle([box_x1, box_y1, box_x2, box_y2], radius=style_config.radius, fill=box_fill_color) # --- رسم متن --- # تراز کردن متن نسبت به نقطه مبنا (base_y1) تا با کوچک شدن کادر، متن جابجا نشود text_start_y = base_y1 + (style_config.paddingY * 0.35) vertical_corr = int(style_config.fontSize * 0.1) current_line_y = text_start_y - vertical_corr global_word_counter = 0 for line_idx, metrics in enumerate(line_metrics): # محاسبه عرض دقیق و واقعی خط برای وسط‌چین شدن کامل درون کادر actual_line_width = sum(metrics["word_widths"]) + max(0, len(metrics["words"]) - 1) * space_w if is_rtl: start_x = (width + actual_line_width) / 2 + style_config.x else: start_x = (width - actual_line_width) / 2 + style_config.x cursor_x = start_x text_y_pos = current_line_y for w_i, word in enumerate(metrics["words"]): w_len = metrics["word_widths"][w_i] word_x = (cursor_x - w_len) if is_rtl else cursor_x # تنظیم رنگ پیش‌فرض current_word_color = base_text_color # --- تغییر جدید: اعمال رنگ کلمه فعال برای تمامی استایل‌های کلاسیک و تایپی --- if style_name in ["white_outline", "plain_white", "classic", "progressive_write"] and active_idx != -1 and global_word_counter == active_idx and getattr(style_config, 'useActiveColor', True): active_hex = style_config.styleActiveColors.get(style_name, '#ffffff') current_word_color = color_parser(active_hex, base_text_color) # ------------------------------------------------------------ current_stroke_fill = stroke_color current_stroke_width = stroke_width 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_this_word = True is_typewriter = getattr(style_config, 'typewriter', False) if style_name == "progressive_write": if active_idx != -1 and global_word_counter > active_idx: should_draw_this_word = False elif 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_this_word = 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 = (cursor_x - sub_w) if is_rtl else word_x if should_draw_this_word and display_word: draw.text((draw_x, text_y_pos), display_word, font=font, fill=current_word_color, stroke_width=current_stroke_width, stroke_fill=current_stroke_fill, direction=getattr(style_config, 'text_direction', 'rtl'), language=getattr(style_config, 'lang_code', 'fa')) # مکان نما باید جلو برود حتی اگر کلمه رسم نشده باشد (تا جای خالی‌اش حفظ شود) if is_rtl: cursor_x -= (w_len + space_w) else: cursor_x += (w_len + space_w) global_word_counter += 1 current_line_y += line_height_px