habulaj commited on
Commit
861f22f
·
verified ·
1 Parent(s): beac3b0

Update routers/image.py

Browse files
Files changed (1) hide show
  1. routers/image.py +139 -11
routers/image.py CHANGED
@@ -4,7 +4,7 @@ from PIL import Image, ImageDraw, ImageFont
4
  from io import BytesIO
5
  import requests
6
  import re
7
- from typing import Optional, List, Tuple
8
 
9
  router = APIRouter()
10
 
@@ -39,6 +39,112 @@ def resize_and_crop_to_fill(img: Image.Image, target_width: int, target_height:
39
  top = (scale_height - target_height) // 2
40
  return img_resized.crop((left, top, left + target_width, top + target_height))
41
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  def create_gradient_overlay(width: int, height: int, positions: List[str], expanded: bool = False) -> Image.Image:
43
  gradient = Image.new("RGBA", (width, height))
44
  draw = ImageDraw.Draw(gradient)
@@ -282,7 +388,7 @@ def add_logo(canvas: Image.Image):
282
  except Exception as e:
283
  print(f"Aviso: Erro ao carregar a logo: {e}")
284
 
285
- def create_canvas(image_url: Optional[str], text: Optional[str], text_position: str = "bottom",
286
  citation: Optional[str] = None, citation_direction: str = "bottom",
287
  text_color: str = "white") -> BytesIO:
288
  width, height = get_device_dimensions()
@@ -306,10 +412,21 @@ def create_canvas(image_url: Optional[str], text: Optional[str], text_position:
306
 
307
  canvas = Image.new("RGBA", (width, height), color=(255, 255, 255, 255))
308
 
 
309
  if image_url:
310
- img = download_image_from_url(image_url)
311
- filled_img = resize_and_crop_to_fill(img, width, height)
312
- canvas.paste(filled_img, (0, 0))
 
 
 
 
 
 
 
 
 
 
313
 
314
  # Determinar posições do gradiente e se precisa expandir
315
  # Só aplicar gradiente se o texto não for preto
@@ -492,7 +609,7 @@ def create_canvas(image_url: Optional[str], text: Optional[str], text_position:
492
  buffer.seek(0)
493
  return buffer
494
 
495
- def create_cover_canvas(image_url: Optional[str], title: Optional[str], title_position: str = "bottom",
496
  text_color: str = "white") -> BytesIO:
497
  width, height = get_device_dimensions()
498
  padding_x, top_padding = 60, 60
@@ -501,10 +618,21 @@ def create_cover_canvas(image_url: Optional[str], title: Optional[str], title_po
501
 
502
  canvas = Image.new("RGBA", (width, height), color=(255, 255, 255, 255))
503
 
 
504
  if image_url:
505
- img = download_image_from_url(image_url)
506
- filled_img = resize_and_crop_to_fill(img, width, height)
507
- canvas.paste(filled_img, (0, 0))
 
 
 
 
 
 
 
 
 
 
508
 
509
  gradient_positions = []
510
  # Só aplicar gradiente se o texto não for preto
@@ -546,7 +674,7 @@ def create_cover_canvas(image_url: Optional[str], title: Optional[str], title_po
546
 
547
  @router.get("/create/image")
548
  def get_news_image(
549
- image_url: Optional[str] = Query(None, description="URL da imagem de fundo"),
550
  text: Optional[str] = Query(None, description="Texto com suporte a tags <strong>"),
551
  text_position: str = Query("bottom", description="Posição do texto: 'top' para topo ou 'bottom' para parte inferior"),
552
  citation: Optional[str] = Query(None, description="Texto da citação"),
@@ -561,7 +689,7 @@ def get_news_image(
561
 
562
  @router.get("/create/cover/image")
563
  def get_cover_image(
564
- image_url: Optional[str] = Query(None, description="URL da imagem de fundo"),
565
  title: Optional[str] = Query(None, description="Título da capa"),
566
  title_position: str = Query("bottom", description="Posição do título: 'top' para topo ou 'bottom' para parte inferior"),
567
  text_color: str = Query("white", description="Cor do texto: 'white' (padrão) ou 'black'. Se 'black', remove o gradiente de fundo")
 
4
  from io import BytesIO
5
  import requests
6
  import re
7
+ from typing import Optional, List, Tuple, Union
8
 
9
  router = APIRouter()
10
 
 
39
  top = (scale_height - target_height) // 2
40
  return img_resized.crop((left, top, left + target_width, top + target_height))
41
 
42
+ def parse_image_urls(image_url_param: Union[str, List[str]]) -> List[str]:
43
+ """Converte o parâmetro de URL(s) em lista de URLs"""
44
+ if isinstance(image_url_param, list):
45
+ return image_url_param
46
+ elif isinstance(image_url_param, str):
47
+ # Se contém vírgulas, divide em múltiplas URLs
48
+ if ',' in image_url_param:
49
+ return [url.strip() for url in image_url_param.split(',') if url.strip()]
50
+ else:
51
+ return [image_url_param]
52
+ return []
53
+
54
+ def create_collage_background(image_urls: List[str], canvas_width: int, canvas_height: int) -> Image.Image:
55
+ """Cria uma colagem como fundo baseada na lista de URLs para Instagram"""
56
+ num_images = len(image_urls)
57
+ border_size = 4 if num_images > 1 else 0 # Linha mais fina e elegante
58
+
59
+ images = [download_image_from_url(url) for url in image_urls]
60
+ canvas = Image.new("RGBA", (canvas_width, canvas_height), (255, 255, 255, 255))
61
+
62
+ if num_images == 1:
63
+ img = resize_and_crop_to_fill(images[0], canvas_width, canvas_height)
64
+ canvas.paste(img, (0, 0))
65
+
66
+ elif num_images == 2:
67
+ # Lado a lado
68
+ slot_width = (canvas_width - border_size) // 2
69
+ img1 = resize_and_crop_to_fill(images[0], slot_width, canvas_height)
70
+ img2 = resize_and_crop_to_fill(images[1], slot_width, canvas_height)
71
+ canvas.paste(img1, (0, 0))
72
+ canvas.paste(img2, (slot_width + border_size, 0))
73
+
74
+ elif num_images == 3:
75
+ # Layout: 2 em cima, 1 embaixo
76
+ half_height = (canvas_height - border_size) // 2
77
+ half_width = (canvas_width - border_size) // 2
78
+ img1 = resize_and_crop_to_fill(images[0], half_width, half_height)
79
+ img2 = resize_and_crop_to_fill(images[1], half_width, half_height)
80
+ img3 = resize_and_crop_to_fill(images[2], canvas_width, half_height)
81
+ canvas.paste(img1, (0, 0))
82
+ canvas.paste(img2, (half_width + border_size, 0))
83
+ canvas.paste(img3, (0, half_height + border_size))
84
+
85
+ elif num_images == 4:
86
+ # Layout 2x2
87
+ half_height = (canvas_height - border_size) // 2
88
+ half_width = (canvas_width - border_size) // 2
89
+ img1 = resize_and_crop_to_fill(images[0], half_width, half_height)
90
+ img2 = resize_and_crop_to_fill(images[1], half_width, half_height)
91
+ img3 = resize_and_crop_to_fill(images[2], half_width, half_height)
92
+ img4 = resize_and_crop_to_fill(images[3], half_width, half_height)
93
+ canvas.paste(img1, (0, 0))
94
+ canvas.paste(img2, (half_width + border_size, 0))
95
+ canvas.paste(img3, (0, half_height + border_size))
96
+ canvas.paste(img4, (half_width + border_size, half_height + border_size))
97
+
98
+ elif num_images == 5:
99
+ # Layout: 2 em cima, 3 embaixo
100
+ top_height = (canvas_height - border_size) * 2 // 5
101
+ bottom_height = canvas_height - top_height - border_size
102
+ half_width = (canvas_width - border_size) // 2
103
+
104
+ img1 = resize_and_crop_to_fill(images[0], half_width, top_height)
105
+ img2 = resize_and_crop_to_fill(images[1], half_width, top_height)
106
+ canvas.paste(img1, (0, 0))
107
+ canvas.paste(img2, (half_width + border_size, 0))
108
+
109
+ y_offset = top_height + border_size
110
+ third_width = (canvas_width - 2 * border_size) // 3
111
+ third_width_last = canvas_width - (third_width * 2 + border_size * 2)
112
+
113
+ img3 = resize_and_crop_to_fill(images[2], third_width, bottom_height)
114
+ img4 = resize_and_crop_to_fill(images[3], third_width, bottom_height)
115
+ img5 = resize_and_crop_to_fill(images[4], third_width_last, bottom_height)
116
+ canvas.paste(img3, (0, y_offset))
117
+ canvas.paste(img4, (third_width + border_size, y_offset))
118
+ canvas.paste(img5, (third_width * 2 + border_size * 2, y_offset))
119
+
120
+ elif num_images == 6:
121
+ # Layout 3x2
122
+ half_height = (canvas_height - border_size) // 2
123
+ third_width = (canvas_width - 2 * border_size) // 3
124
+ third_width_last = canvas_width - (third_width * 2 + border_size * 2)
125
+
126
+ # Primeira linha
127
+ img1 = resize_and_crop_to_fill(images[0], third_width, half_height)
128
+ img2 = resize_and_crop_to_fill(images[1], third_width, half_height)
129
+ img3 = resize_and_crop_to_fill(images[2], third_width, half_height)
130
+ canvas.paste(img1, (0, 0))
131
+ canvas.paste(img2, (third_width + border_size, 0))
132
+ canvas.paste(img3, (third_width * 2 + border_size * 2, 0))
133
+
134
+ # Segunda linha
135
+ y_offset = half_height + border_size
136
+ img4 = resize_and_crop_to_fill(images[3], third_width, half_height)
137
+ img5 = resize_and_crop_to_fill(images[4], third_width, half_height)
138
+ img6 = resize_and_crop_to_fill(images[5], third_width_last, half_height)
139
+ canvas.paste(img4, (0, y_offset))
140
+ canvas.paste(img5, (third_width + border_size, y_offset))
141
+ canvas.paste(img6, (third_width * 2 + border_size * 2, y_offset))
142
+
143
+ else:
144
+ raise HTTPException(status_code=400, detail="Apenas até 6 imagens são suportadas.")
145
+
146
+ return canvas
147
+
148
  def create_gradient_overlay(width: int, height: int, positions: List[str], expanded: bool = False) -> Image.Image:
149
  gradient = Image.new("RGBA", (width, height))
150
  draw = ImageDraw.Draw(gradient)
 
388
  except Exception as e:
389
  print(f"Aviso: Erro ao carregar a logo: {e}")
390
 
391
+ def create_canvas(image_url: Optional[Union[str, List[str]]], text: Optional[str], text_position: str = "bottom",
392
  citation: Optional[str] = None, citation_direction: str = "bottom",
393
  text_color: str = "white") -> BytesIO:
394
  width, height = get_device_dimensions()
 
412
 
413
  canvas = Image.new("RGBA", (width, height), color=(255, 255, 255, 255))
414
 
415
+ # Processar imagem(s) - suporte para colagem
416
  if image_url:
417
+ parsed_urls = parse_image_urls(image_url)
418
+ if parsed_urls:
419
+ if len(parsed_urls) > 6:
420
+ raise HTTPException(status_code=400, detail="Máximo de 6 imagens permitidas")
421
+
422
+ if len(parsed_urls) == 1:
423
+ # Uma única imagem - comportamento original
424
+ img = download_image_from_url(parsed_urls[0])
425
+ filled_img = resize_and_crop_to_fill(img, width, height)
426
+ canvas.paste(filled_img, (0, 0))
427
+ else:
428
+ # Múltiplas imagens - criar colagem
429
+ canvas = create_collage_background(parsed_urls, width, height)
430
 
431
  # Determinar posições do gradiente e se precisa expandir
432
  # Só aplicar gradiente se o texto não for preto
 
609
  buffer.seek(0)
610
  return buffer
611
 
612
+ def create_cover_canvas(image_url: Optional[Union[str, List[str]]], title: Optional[str], title_position: str = "bottom",
613
  text_color: str = "white") -> BytesIO:
614
  width, height = get_device_dimensions()
615
  padding_x, top_padding = 60, 60
 
618
 
619
  canvas = Image.new("RGBA", (width, height), color=(255, 255, 255, 255))
620
 
621
+ # Processar imagem(s) - suporte para colagem
622
  if image_url:
623
+ parsed_urls = parse_image_urls(image_url)
624
+ if parsed_urls:
625
+ if len(parsed_urls) > 6:
626
+ raise HTTPException(status_code=400, detail="Máximo de 6 imagens permitidas")
627
+
628
+ if len(parsed_urls) == 1:
629
+ # Uma única imagem - comportamento original
630
+ img = download_image_from_url(parsed_urls[0])
631
+ filled_img = resize_and_crop_to_fill(img, width, height)
632
+ canvas.paste(filled_img, (0, 0))
633
+ else:
634
+ # Múltiplas imagens - criar colagem
635
+ canvas = create_collage_background(parsed_urls, width, height)
636
 
637
  gradient_positions = []
638
  # Só aplicar gradiente se o texto não for preto
 
674
 
675
  @router.get("/create/image")
676
  def get_news_image(
677
+ image_url: Optional[Union[str, List[str]]] = Query(None, description="URL da imagem ou lista de URLs separadas por vírgula para colagem (máximo 6)"),
678
  text: Optional[str] = Query(None, description="Texto com suporte a tags <strong>"),
679
  text_position: str = Query("bottom", description="Posição do texto: 'top' para topo ou 'bottom' para parte inferior"),
680
  citation: Optional[str] = Query(None, description="Texto da citação"),
 
689
 
690
  @router.get("/create/cover/image")
691
  def get_cover_image(
692
+ image_url: Optional[Union[str, List[str]]] = Query(None, description="URL da imagem ou lista de URLs separadas por vírgula para colagem (máximo 6)"),
693
  title: Optional[str] = Query(None, description="Título da capa"),
694
  title_position: str = Query("bottom", description="Posição do título: 'top' para topo ou 'bottom' para parte inferior"),
695
  text_color: str = Query("white", description="Cor do texto: 'white' (padrão) ou 'black'. Se 'black', remove o gradiente de fundo")