| 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 |
|
|
| |
| 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: |
| |
| 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')) |