Spaces:
Running
Running
capradeepgujaran
commited on
Commit
•
1524f36
1
Parent(s):
d834ac9
Update app.py
Browse files
app.py
CHANGED
@@ -391,15 +391,20 @@ class CertificateGenerator:
|
|
391 |
def _add_photo(self, certificate: Image.Image, photo_path: str):
|
392 |
"""Add a clear circular profile photo in the top-right corner"""
|
393 |
try:
|
|
|
|
|
|
|
|
|
394 |
# Open and process photo
|
395 |
photo = Image.open(photo_path)
|
|
|
396 |
|
397 |
-
# Define size for circular photo
|
398 |
size = (120, 120)
|
399 |
|
400 |
-
# Convert to
|
401 |
-
if photo.mode
|
402 |
-
photo = photo.convert('
|
403 |
|
404 |
# Create high-quality circular mask
|
405 |
mask = Image.new('L', size, 0)
|
@@ -409,47 +414,43 @@ class CertificateGenerator:
|
|
409 |
# Resize photo maintaining aspect ratio
|
410 |
aspect = photo.width / photo.height
|
411 |
if aspect > 1:
|
412 |
-
# Width is greater
|
413 |
new_height = size[1]
|
414 |
new_width = int(new_height * aspect)
|
415 |
-
photo = photo.resize((new_width, new_height), Image.Resampling.LANCZOS)
|
416 |
-
# Center crop
|
417 |
-
left = (new_width - size[0]) // 2
|
418 |
-
photo = photo.crop((left, 0, left + size[0], size[1]))
|
419 |
else:
|
420 |
-
# Height is greater
|
421 |
new_width = size[0]
|
422 |
new_height = int(new_width / aspect)
|
423 |
-
|
424 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
425 |
top = (new_height - size[1]) // 2
|
426 |
photo = photo.crop((0, top, size[0], top + size[1]))
|
427 |
|
428 |
-
# Create
|
429 |
output = Image.new('RGBA', size, (0, 0, 0, 0))
|
430 |
output.paste(photo, (0, 0))
|
431 |
-
|
432 |
-
# Apply antialiasing to the mask
|
433 |
-
mask = mask.filter(ImageFilter.GaussianBlur(radius=1))
|
434 |
output.putalpha(mask)
|
435 |
|
436 |
-
# Position in top-right corner
|
437 |
-
|
438 |
-
|
439 |
-
position = (certificate.width - size[0] - padding_right, padding_top)
|
440 |
|
441 |
-
# Add
|
442 |
bg = Image.new('RGBA', size, (255, 255, 255, 255))
|
443 |
-
|
444 |
-
certificate.paste(bg, position, mask=bg_mask)
|
445 |
|
446 |
# Paste the photo
|
447 |
-
certificate.paste(output,
|
448 |
-
|
|
|
449 |
except Exception as e:
|
450 |
print(f"Error adding photo: {str(e)}")
|
451 |
-
|
452 |
-
|
453 |
|
454 |
def generate(
|
455 |
self,
|
@@ -459,43 +460,37 @@ class CertificateGenerator:
|
|
459 |
company_logo: Optional[str] = None,
|
460 |
participant_photo: Optional[str] = None
|
461 |
) -> str:
|
462 |
-
"""Generate certificate with improved
|
463 |
try:
|
464 |
-
|
|
|
465 |
draw = ImageDraw.Draw(certificate)
|
466 |
|
467 |
-
# Add
|
468 |
self._add_professional_border(draw)
|
469 |
|
470 |
-
# Add photo if provided (now centered)
|
471 |
-
if participant_photo:
|
472 |
-
self._add_photo(certificate, participant_photo)
|
473 |
-
# Adjust vertical spacing for content when photo is present
|
474 |
-
content_start_y = 180 # Increased to accommodate photo
|
475 |
-
else:
|
476 |
-
content_start_y = 140 # Original spacing when no photo
|
477 |
-
|
478 |
# Load fonts
|
479 |
fonts = self._load_fonts()
|
480 |
|
481 |
-
# Add content with adjusted vertical position
|
482 |
-
self._add_content(
|
483 |
-
draw,
|
484 |
-
fonts,
|
485 |
-
str(name),
|
486 |
-
str(course_name),
|
487 |
-
float(score),
|
488 |
-
y_offset=content_start_y
|
489 |
-
)
|
490 |
-
|
491 |
# Add company logo if provided
|
492 |
-
if company_logo:
|
493 |
self._add_logo(certificate, company_logo)
|
494 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
495 |
return self._save_certificate(certificate)
|
496 |
|
497 |
except Exception as e:
|
498 |
-
print(f"Error generating certificate: {e}")
|
|
|
|
|
499 |
return None
|
500 |
|
501 |
def _create_base_certificate(self) -> Image.Image:
|
@@ -516,10 +511,15 @@ class CertificateGenerator:
|
|
516 |
return certificate
|
517 |
|
518 |
def _save_certificate(self, certificate: Image.Image) -> str:
|
519 |
-
|
520 |
-
|
521 |
-
|
522 |
-
|
|
|
|
|
|
|
|
|
|
|
523 |
|
524 |
class QuizApp:
|
525 |
def __init__(self, api_key: str):
|
|
|
391 |
def _add_photo(self, certificate: Image.Image, photo_path: str):
|
392 |
"""Add a clear circular profile photo in the top-right corner"""
|
393 |
try:
|
394 |
+
if not photo_path or not os.path.exists(photo_path):
|
395 |
+
print(f"Photo path does not exist: {photo_path}")
|
396 |
+
return
|
397 |
+
|
398 |
# Open and process photo
|
399 |
photo = Image.open(photo_path)
|
400 |
+
print(f"Loaded photo: {photo.size}, {photo.mode}") # Debug info
|
401 |
|
402 |
+
# Define size for circular photo
|
403 |
size = (120, 120)
|
404 |
|
405 |
+
# Convert to RGB if not already
|
406 |
+
if photo.mode not in ('RGB', 'RGBA'):
|
407 |
+
photo = photo.convert('RGB')
|
408 |
|
409 |
# Create high-quality circular mask
|
410 |
mask = Image.new('L', size, 0)
|
|
|
414 |
# Resize photo maintaining aspect ratio
|
415 |
aspect = photo.width / photo.height
|
416 |
if aspect > 1:
|
|
|
417 |
new_height = size[1]
|
418 |
new_width = int(new_height * aspect)
|
|
|
|
|
|
|
|
|
419 |
else:
|
|
|
420 |
new_width = size[0]
|
421 |
new_height = int(new_width / aspect)
|
422 |
+
|
423 |
+
photo = photo.resize((new_width, max(new_height, 1)), Image.Resampling.LANCZOS)
|
424 |
+
|
425 |
+
# Center crop
|
426 |
+
if aspect > 1:
|
427 |
+
left = (new_width - size[0]) // 2
|
428 |
+
photo = photo.crop((left, 0, left + size[0], size[1]))
|
429 |
+
else:
|
430 |
top = (new_height - size[1]) // 2
|
431 |
photo = photo.crop((0, top, size[0], top + size[1]))
|
432 |
|
433 |
+
# Create circular photo
|
434 |
output = Image.new('RGBA', size, (0, 0, 0, 0))
|
435 |
output.paste(photo, (0, 0))
|
|
|
|
|
|
|
436 |
output.putalpha(mask)
|
437 |
|
438 |
+
# Position in top-right corner
|
439 |
+
photo_x = certificate.width - size[0] - 60 # 60px from right
|
440 |
+
photo_y = 40 # 40px from top
|
|
|
441 |
|
442 |
+
# Add white background circle
|
443 |
bg = Image.new('RGBA', size, (255, 255, 255, 255))
|
444 |
+
certificate.paste(bg, (photo_x, photo_y), mask=mask)
|
|
|
445 |
|
446 |
# Paste the photo
|
447 |
+
certificate.paste(output, (photo_x, photo_y), mask=output)
|
448 |
+
print(f"Successfully added photo at position ({photo_x}, {photo_y})")
|
449 |
+
|
450 |
except Exception as e:
|
451 |
print(f"Error adding photo: {str(e)}")
|
452 |
+
import traceback
|
453 |
+
traceback.print_exc()
|
454 |
|
455 |
def generate(
|
456 |
self,
|
|
|
460 |
company_logo: Optional[str] = None,
|
461 |
participant_photo: Optional[str] = None
|
462 |
) -> str:
|
463 |
+
"""Generate certificate with improved photo handling"""
|
464 |
try:
|
465 |
+
# Create base certificate
|
466 |
+
certificate = Image.new('RGB', self.certificate_size, self.background_color)
|
467 |
draw = ImageDraw.Draw(certificate)
|
468 |
|
469 |
+
# Add border
|
470 |
self._add_professional_border(draw)
|
471 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
472 |
# Load fonts
|
473 |
fonts = self._load_fonts()
|
474 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
475 |
# Add company logo if provided
|
476 |
+
if company_logo and os.path.exists(company_logo):
|
477 |
self._add_logo(certificate, company_logo)
|
478 |
|
479 |
+
# Add participant photo if provided
|
480 |
+
if participant_photo:
|
481 |
+
print(f"Processing photo: {participant_photo}") # Debug info
|
482 |
+
self._add_photo(certificate, participant_photo)
|
483 |
+
|
484 |
+
# Add content
|
485 |
+
self._add_content(draw, fonts, str(name), str(course_name), float(score))
|
486 |
+
|
487 |
+
# Save certificate
|
488 |
return self._save_certificate(certificate)
|
489 |
|
490 |
except Exception as e:
|
491 |
+
print(f"Error generating certificate: {str(e)}")
|
492 |
+
import traceback
|
493 |
+
traceback.print_exc()
|
494 |
return None
|
495 |
|
496 |
def _create_base_certificate(self) -> Image.Image:
|
|
|
511 |
return certificate
|
512 |
|
513 |
def _save_certificate(self, certificate: Image.Image) -> str:
|
514 |
+
"""Save certificate with improved error handling"""
|
515 |
+
try:
|
516 |
+
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.png')
|
517 |
+
certificate.save(temp_file.name, 'PNG', quality=95)
|
518 |
+
print(f"Certificate saved to: {temp_file.name}") # Debug info
|
519 |
+
return temp_file.name
|
520 |
+
except Exception as e:
|
521 |
+
print(f"Error saving certificate: {str(e)}")
|
522 |
+
return None
|
523 |
|
524 |
class QuizApp:
|
525 |
def __init__(self, api_key: str):
|