import cv2 import textwrap from PIL import Image, ImageDraw, ImageFont import numpy as np def add_text( image, text, contour, font_path, initial_font_size=42, padding=10, line_spacing=1.25 ): """ Renderiza texto dentro de uma bolha usando o CONTOUR como referência. Prioriza quebra de linhas antes de reduzir a fonte. """ # --- Extrai bounding box do contorno --- x, y, w, h = cv2.boundingRect(contour) img_pil = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB)) draw = ImageDraw.Draw(img_pil) font_size = initial_font_size wrapping_ratio = 0.9 while font_size > 10: font = ImageFont.truetype(font_path, font_size) max_chars_per_line = max( 1, int((w - 2 * padding) / (font_size * wrapping_ratio)) ) lines = textwrap.wrap(text, width=max_chars_per_line) # Evita linha única quando há espaço vertical min_lines = 2 if h > font_size * 2.5 else 1 if len(lines) < min_lines: wrapping_ratio -= 0.03 continue max_line_width = 0 total_height = 0 for line in lines: bbox_text = draw.textbbox((0, 0), line, font=font) lw = bbox_text[2] - bbox_text[0] lh = bbox_text[3] - bbox_text[1] max_line_width = max(max_line_width, lw) total_height += lh total_height = int(total_height * line_spacing) if ( total_height <= (h - 2 * padding) and max_line_width <= (w - 2 * padding) ): break if max_line_width > (w - 2 * padding): wrapping_ratio -= 0.02 else: font_size -= 1 # --- Centralização --- current_y = y + padding + (h - total_height) // 2 for line in lines: bbox_text = draw.textbbox((0, 0), line, font=font) lw = bbox_text[2] - bbox_text[0] lh = bbox_text[3] - bbox_text[1] text_x = x + (w - lw) // 2 draw.text((text_x, current_y), line, fill=(0, 0, 0), font=font) current_y += int(lh * line_spacing) return cv2.cvtColor(np.array(img_pil), cv2.COLOR_RGB2BGR)