Spaces:
Running
Running
Update routers/image.py
Browse files- routers/image.py +60 -10
routers/image.py
CHANGED
|
@@ -427,9 +427,9 @@ def calculate_text_height(text: str, font, max_width: int) -> int:
|
|
| 427 |
|
| 428 |
return total_height
|
| 429 |
|
| 430 |
-
def render_slide_text(draw: ImageDraw.Draw, text: str, x: int, y: int, max_width: int, max_lines: int = 5, center_vertical: bool = False) -> None:
|
| 431 |
"""
|
| 432 |
-
Renderiza texto para slides com fonte padrão,
|
| 433 |
Suporta formatação com asteriscos para negrito.
|
| 434 |
|
| 435 |
Args:
|
|
@@ -440,6 +440,7 @@ def render_slide_text(draw: ImageDraw.Draw, text: str, x: int, y: int, max_width
|
|
| 440 |
max_width: Largura máxima do texto
|
| 441 |
max_lines: Número máximo de linhas (padrão: 5)
|
| 442 |
center_vertical: Se True, centraliza verticalmente o texto na posição Y
|
|
|
|
| 443 |
"""
|
| 444 |
import re
|
| 445 |
|
|
@@ -642,7 +643,7 @@ def render_slide_text(draw: ImageDraw.Draw, text: str, x: int, y: int, max_width
|
|
| 642 |
current_x = x
|
| 643 |
for i, (segment_text, segment_font) in enumerate(line):
|
| 644 |
font_to_use = final_regular_font if segment_font == regular_font else final_bold_font
|
| 645 |
-
draw.text((current_x, current_y), segment_text, fill=
|
| 646 |
bbox = font_to_use.getbbox(segment_text)
|
| 647 |
current_x += bbox[2] - bbox[0]
|
| 648 |
|
|
@@ -657,19 +658,64 @@ def render_slide_text(draw: ImageDraw.Draw, text: str, x: int, y: int, max_width
|
|
| 657 |
# Aplicar line height de 120% (adicionar 20% de espaçamento extra)
|
| 658 |
current_y += int(max_line_height * 1.20)
|
| 659 |
|
| 660 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 661 |
"""
|
| 662 |
Cria um canvas para slides com imagem cortada baseada no parâmetro slide.
|
| 663 |
|
| 664 |
Args:
|
| 665 |
image_url: URL da imagem de fundo
|
| 666 |
slide: 1 para cortar início (880px), 2 para cortar final (400px)
|
|
|
|
|
|
|
| 667 |
"""
|
| 668 |
# Dimensões do poster
|
| 669 |
canvas_width, canvas_height = 1080, 1350
|
| 670 |
|
| 671 |
-
#
|
| 672 |
-
|
|
|
|
|
|
|
|
|
|
| 673 |
|
| 674 |
# Baixar imagem original
|
| 675 |
img = download_image_from_url(image_url)
|
|
@@ -743,11 +789,14 @@ def create_slide_canvas(image_url: str, slide: int, text: Optional[str] = None)
|
|
| 743 |
# Colar imagem no canvas
|
| 744 |
canvas.paste(img_with_mask, (x_pos, y_pos), img_with_mask)
|
| 745 |
|
|
|
|
|
|
|
|
|
|
| 746 |
# Adicionar texto se slide for 1 e text for fornecido
|
| 747 |
if slide == 1 and text and text.strip():
|
| 748 |
draw = ImageDraw.Draw(canvas)
|
| 749 |
# Renderizar texto com as especificações: X: 200, Y: 210, L: 815, MAX 5 linhas
|
| 750 |
-
render_slide_text(draw, text, x=200, y=210, max_width=815, max_lines=5)
|
| 751 |
|
| 752 |
# Adicionar texto se slide for 2 e text for fornecido
|
| 753 |
if slide == 2 and text and text.strip():
|
|
@@ -755,7 +804,7 @@ def create_slide_canvas(image_url: str, slide: int, text: Optional[str] = None)
|
|
| 755 |
# Renderizar texto com as especificações: X: 445, Y: 430, L: 585, A: 748, MAX 19 linhas
|
| 756 |
# A imagem do slide 2 vai de Y: 425 a Y: 1205 (altura 780px)
|
| 757 |
# Para centralizar o texto na área da imagem, o centro é Y: 425 + (780/2) = 815
|
| 758 |
-
render_slide_text(draw, text, x=445, y=815, max_width=585, max_lines=19, center_vertical=True)
|
| 759 |
|
| 760 |
buffer = BytesIO()
|
| 761 |
canvas.convert("RGB").save(buffer, format="PNG")
|
|
@@ -887,12 +936,13 @@ def get_image(
|
|
| 887 |
citation: Optional[str] = Query(None, description="Citação a ser exibida entre aspas (fonte Cheltenham Normal 700)"),
|
| 888 |
text_position: str = Query("bottom", description="Posição do texto: 'top' ou 'bottom'"),
|
| 889 |
citation_position: str = Query("top", description="Posição da citação: 'top' ou 'bottom'"),
|
| 890 |
-
slide: Optional[int] = Query(None, description="Número do slide: 1 para início (880px), 2 para final (400px)")
|
|
|
|
| 891 |
):
|
| 892 |
try:
|
| 893 |
# Se o parâmetro slide for fornecido, usar a funcionalidade de slides
|
| 894 |
if slide is not None:
|
| 895 |
-
buffer = create_slide_canvas(image_url, slide, text)
|
| 896 |
else:
|
| 897 |
# Usar funcionalidade normal de criação de imagem
|
| 898 |
buffer = create_canvas(image_url, text, citation, text_position, citation_position)
|
|
|
|
| 427 |
|
| 428 |
return total_height
|
| 429 |
|
| 430 |
+
def render_slide_text(draw: ImageDraw.Draw, text: str, x: int, y: int, max_width: int, max_lines: int = 5, center_vertical: bool = False, text_color: tuple = (0, 0, 0, 255)) -> None:
|
| 431 |
"""
|
| 432 |
+
Renderiza texto para slides com fonte padrão, tamanho inicial 32px.
|
| 433 |
Suporta formatação com asteriscos para negrito.
|
| 434 |
|
| 435 |
Args:
|
|
|
|
| 440 |
max_width: Largura máxima do texto
|
| 441 |
max_lines: Número máximo de linhas (padrão: 5)
|
| 442 |
center_vertical: Se True, centraliza verticalmente o texto na posição Y
|
| 443 |
+
text_color: Cor do texto como tuple (R, G, B, A)
|
| 444 |
"""
|
| 445 |
import re
|
| 446 |
|
|
|
|
| 643 |
current_x = x
|
| 644 |
for i, (segment_text, segment_font) in enumerate(line):
|
| 645 |
font_to_use = final_regular_font if segment_font == regular_font else final_bold_font
|
| 646 |
+
draw.text((current_x, current_y), segment_text, fill=text_color, font=font_to_use)
|
| 647 |
bbox = font_to_use.getbbox(segment_text)
|
| 648 |
current_x += bbox[2] - bbox[0]
|
| 649 |
|
|
|
|
| 658 |
# Aplicar line height de 120% (adicionar 20% de espaçamento extra)
|
| 659 |
current_y += int(max_line_height * 1.20)
|
| 660 |
|
| 661 |
+
def get_slide_color(slide_color: int) -> tuple:
|
| 662 |
+
"""
|
| 663 |
+
Retorna a cor do slide baseada no número fornecido.
|
| 664 |
+
|
| 665 |
+
Args:
|
| 666 |
+
slide_color: Número da cor (1-8)
|
| 667 |
+
|
| 668 |
+
Returns:
|
| 669 |
+
Tuple (R, G, B, A) da cor
|
| 670 |
+
"""
|
| 671 |
+
color_map = {
|
| 672 |
+
1: (0, 0, 0, 255), # #000000 - Preto
|
| 673 |
+
2: (191, 198, 147, 255), # #BFC693
|
| 674 |
+
3: (246, 164, 29, 255), # #f6a41d
|
| 675 |
+
4: (245, 244, 239, 255), # #f5f4ef
|
| 676 |
+
5: (111, 180, 165, 255), # #6fb4a5
|
| 677 |
+
6: (25, 83, 208, 255), # #1953d0
|
| 678 |
+
7: (255, 80, 17, 255), # #ff5011
|
| 679 |
+
8: (255, 217, 194, 255), # #ffd9c2
|
| 680 |
+
}
|
| 681 |
+
|
| 682 |
+
# Se o número for inválido, usar preto como padrão
|
| 683 |
+
return color_map.get(slide_color, (0, 0, 0, 255))
|
| 684 |
+
|
| 685 |
+
def get_text_color(slide_color: int) -> tuple:
|
| 686 |
+
"""
|
| 687 |
+
Retorna a cor do texto baseada na cor do slide.
|
| 688 |
+
|
| 689 |
+
Args:
|
| 690 |
+
slide_color: Número da cor do slide (1-8)
|
| 691 |
+
|
| 692 |
+
Returns:
|
| 693 |
+
Tuple (R, G, B, A) da cor do texto
|
| 694 |
+
"""
|
| 695 |
+
# Se for cor 1 (preto), usar texto branco, senão usar texto preto
|
| 696 |
+
if slide_color == 1:
|
| 697 |
+
return (255, 255, 255, 255) # Branco
|
| 698 |
+
else:
|
| 699 |
+
return (0, 0, 0, 255) # Preto
|
| 700 |
+
|
| 701 |
+
def create_slide_canvas(image_url: str, slide: int, text: Optional[str] = None, slide_color: int = 1) -> BytesIO:
|
| 702 |
"""
|
| 703 |
Cria um canvas para slides com imagem cortada baseada no parâmetro slide.
|
| 704 |
|
| 705 |
Args:
|
| 706 |
image_url: URL da imagem de fundo
|
| 707 |
slide: 1 para cortar início (880px), 2 para cortar final (400px)
|
| 708 |
+
text: Texto opcional para exibir no slide
|
| 709 |
+
slide_color: Cor do slide (1-8)
|
| 710 |
"""
|
| 711 |
# Dimensões do poster
|
| 712 |
canvas_width, canvas_height = 1080, 1350
|
| 713 |
|
| 714 |
+
# Obter cor do slide
|
| 715 |
+
slide_bg_color = get_slide_color(slide_color)
|
| 716 |
+
|
| 717 |
+
# Criar canvas com cor do slide
|
| 718 |
+
canvas = Image.new("RGBA", (canvas_width, canvas_height), color=slide_bg_color)
|
| 719 |
|
| 720 |
# Baixar imagem original
|
| 721 |
img = download_image_from_url(image_url)
|
|
|
|
| 789 |
# Colar imagem no canvas
|
| 790 |
canvas.paste(img_with_mask, (x_pos, y_pos), img_with_mask)
|
| 791 |
|
| 792 |
+
# Obter cor do texto baseada na cor do slide
|
| 793 |
+
text_color = get_text_color(slide_color)
|
| 794 |
+
|
| 795 |
# Adicionar texto se slide for 1 e text for fornecido
|
| 796 |
if slide == 1 and text and text.strip():
|
| 797 |
draw = ImageDraw.Draw(canvas)
|
| 798 |
# Renderizar texto com as especificações: X: 200, Y: 210, L: 815, MAX 5 linhas
|
| 799 |
+
render_slide_text(draw, text, x=200, y=210, max_width=815, max_lines=5, text_color=text_color)
|
| 800 |
|
| 801 |
# Adicionar texto se slide for 2 e text for fornecido
|
| 802 |
if slide == 2 and text and text.strip():
|
|
|
|
| 804 |
# Renderizar texto com as especificações: X: 445, Y: 430, L: 585, A: 748, MAX 19 linhas
|
| 805 |
# A imagem do slide 2 vai de Y: 425 a Y: 1205 (altura 780px)
|
| 806 |
# Para centralizar o texto na área da imagem, o centro é Y: 425 + (780/2) = 815
|
| 807 |
+
render_slide_text(draw, text, x=445, y=815, max_width=585, max_lines=19, center_vertical=True, text_color=text_color)
|
| 808 |
|
| 809 |
buffer = BytesIO()
|
| 810 |
canvas.convert("RGB").save(buffer, format="PNG")
|
|
|
|
| 936 |
citation: Optional[str] = Query(None, description="Citação a ser exibida entre aspas (fonte Cheltenham Normal 700)"),
|
| 937 |
text_position: str = Query("bottom", description="Posição do texto: 'top' ou 'bottom'"),
|
| 938 |
citation_position: str = Query("top", description="Posição da citação: 'top' ou 'bottom'"),
|
| 939 |
+
slide: Optional[int] = Query(None, description="Número do slide: 1 para início (880px), 2 para final (400px)"),
|
| 940 |
+
slide_color: Optional[int] = Query(1, description="Cor do slide: 1=#000000, 2=#BFC693, 3=#f6a41d, 4=#f5f4ef, 5=#6fb4a5, 6=#1953d0, 7=#ff5011, 8=#ffd9c2")
|
| 941 |
):
|
| 942 |
try:
|
| 943 |
# Se o parâmetro slide for fornecido, usar a funcionalidade de slides
|
| 944 |
if slide is not None:
|
| 945 |
+
buffer = create_slide_canvas(image_url, slide, text, slide_color)
|
| 946 |
else:
|
| 947 |
# Usar funcionalidade normal de criação de imagem
|
| 948 |
buffer = create_canvas(image_url, text, citation, text_position, citation_position)
|