11import logging
2+ import tempfile
3+ import os
24from typing import List
3- from models .participant import Participant
45from PIL import Image , ImageDraw , ImageFont
56from io import BytesIO
6- import os
7+ from models . participant import Participant
78from certified_builder .utils .fetch_file_certificate import fetch_file_certificate
8- import tempfile
9+ from certified_builder .certificates_on_solana import CertificatesOnSolana
10+ from certified_builder .make_qrcode import MakeQRCode
11+
912FONT_NAME = os .path .join (os .path .dirname (__file__ ), "fonts/PinyonScript/PinyonScript-Regular.ttf" )
1013VALIDATION_CODE = os .path .join (os .path .dirname (__file__ ), "fonts/ChakraPetch/ChakraPetch-SemiBold.ttf" )
1114DETAILS_FONT = os .path .join (os .path .dirname (__file__ ), "fonts/ChakraPetch/ChakraPetch-Regular.ttf" )
@@ -44,6 +47,25 @@ def build_certificates(self, participants: List[Participant]):
4447
4548 for participant in participants :
4649 try :
50+ # Register certificate on Solana, with returned data extract url for verification
51+ solana_response = CertificatesOnSolana .register_certificate_on_solana (
52+ certificate_data = {
53+ "name" : participant .name_completed (),
54+ "event" : participant .event .product_name ,
55+ "email" : participant .email ,
56+ "certificate_code" : participant .formated_validation_code ()
57+ }
58+ )
59+ # solana_response = {
60+ # "blockchain": {
61+ # "verificacao_url": "https://www.google.com"
62+ # }
63+ # }
64+ participant .authenticity_verification_url = solana_response .get ("blockchain" , {}).get ("verificacao_url" , "" )
65+
66+ if not participant .authenticity_verification_url :
67+ raise RuntimeError ("Failed to get authenticity verification URL from Solana response" )
68+
4769 # Download template and logo only if they are not shared
4870 if not all_same_background :
4971 certificate_template = self ._download_image (participant .certificate .background )
@@ -111,9 +133,10 @@ def generate_certificate(self, participant: Participant, certificate_template: I
111133 # Create transparent layer for text and logo
112134 overlay = Image .new ("RGBA" , certificate_template .size , (255 , 255 , 255 , 0 ))
113135
114- # Optimize logo size
115- logo_size = (150 , 150 )
116- logo = logo .resize (logo_size , Image .Resampling .LANCZOS )
136+ # Optimize logo size (evita upscaling para reduzir pixelização)
137+ logo_max_size = (150 , 150 )
138+ if logo .width > logo_max_size [0 ] or logo .height > logo_max_size [1 ]:
139+ logo .thumbnail (logo_max_size , Image .Resampling .LANCZOS )
117140
118141 # Paste logo - handle potential transparency issues
119142 try :
@@ -124,6 +147,40 @@ def generate_certificate(self, participant: Participant, certificate_template: I
124147 # Fallback without using the logo as its own mask
125148 overlay .paste (logo , (50 , 50 ))
126149
150+
151+ qrcode_size = (150 , 150 )
152+ qr_code_image_io = MakeQRCode .generate_qr_code (participant .authenticity_verification_url )
153+ qr_code_image = Image .open (qr_code_image_io ).convert ("RGBA" )
154+ # comentário: para manter o QR nítido, usamos NEAREST ao redimensionar
155+ if qr_code_image .size != qrcode_size :
156+ qr_code_image = qr_code_image .resize (qrcode_size , Image .Resampling .NEAREST )
157+
158+ # Add QR code to overlay
159+ # preciso que a posição do QR code seja abaixo do logo, alinhado à esquerda
160+ overlay .paste (qr_code_image , (50 , 200 ), qr_code_image )
161+
162+ # Add "Scan to Validate" text below the QR code
163+ # comentário: camada de texto criada para ficar logo abaixo do QR code, centralizada ao QR e com espaçamento justo
164+ try :
165+ # calcula centralização do texto com base na largura do QR
166+ tmp_img = Image .new ("RGBA" , certificate_template .size , (255 , 255 , 255 , 0 ))
167+ tmp_draw = ImageDraw .Draw (tmp_img )
168+ tmp_font = ImageFont .truetype (DETAILS_FONT , 16 )
169+ text_bbox = tmp_draw .textbbox ((0 , 0 ), "Scan to Validate" , font = tmp_font )
170+ text_w = text_bbox [2 ] - text_bbox [0 ]
171+ text_x = 50 + int ((qrcode_size [0 ] - text_w ) / 2 )
172+ text_y = 185 + qrcode_size [1 ] # espaçamento curto (quase colado)
173+
174+ scan_text_image = self .create_scan_to_validate_image (
175+ size = certificate_template .size ,
176+ position = (text_x , text_y )
177+ )
178+ overlay .paste (scan_text_image , (0 , 0 ), scan_text_image )
179+ logger .info ("Texto 'Scan to Validate' adicionado abaixo do QR code" )
180+ except Exception as e :
181+ logger .warning (f"Falha ao adicionar texto 'Scan to Validate': { str (e )} " )
182+
183+
127184 # Add name
128185 name_image = self .create_name_image (participant .name_completed (), certificate_template .size )
129186
@@ -243,6 +300,19 @@ def create_validation_code_image(self, validation_code: str, size: tuple) -> Ima
243300 logger .error (f"Erro ao criar imagem do código de validação: { str (e )} " )
244301 raise
245302
303+ def create_scan_to_validate_image (self , size : tuple , position : tuple ) -> Image :
304+ """Create image with the 'Scan to Validate' label using DETAILS_FONT at a given position."""
305+ try :
306+ # comentário: imagem transparente do tamanho do canvas com o texto posicionado
307+ text_image = Image .new ("RGBA" , size , (255 , 255 , 255 , 0 ))
308+ draw = ImageDraw .Draw (text_image )
309+ font = ImageFont .truetype (DETAILS_FONT , 16 )
310+ draw .text (position , "Scan to Validate" , fill = TEXT_COLOR , font = font )
311+ return text_image
312+ except Exception as e :
313+ logger .error (f"Erro ao criar imagem do texto 'Scan to Validate': { str (e )} " )
314+ raise
315+
246316 def calculate_text_position (self , text : str , font : ImageFont , draw : ImageDraw , size : tuple ) -> tuple :
247317 """Calculate centered position for text."""
248318 text_bbox = draw .textbbox ((0 , 0 ), text , font = font )
0 commit comments