capradeepgujaran commited on
Commit
be16a8e
1 Parent(s): 13df159

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +94 -115
app.py CHANGED
@@ -204,107 +204,11 @@ class QuizGenerationError(Exception):
204
  """Exception raised for errors in quiz generation"""
205
  pass
206
 
207
-
208
- class CertificateGenerator:
209
- def __init__(self):
210
- self.certificate_size = (1200, 800)
211
- self.border_color = '#4682B4'
212
- self.background_color = '#F0F8FF'
213
-
214
- # Install fonts if needed
215
- FontManager.install_fonts()
216
- self.font_paths = FontManager.get_font_paths()
217
-
218
- def _load_fonts(self) -> Dict[str, ImageFont.FreeTypeFont]:
219
- """Load fonts with fallbacks"""
220
- fonts = {}
221
- try:
222
- if self.font_paths['regular'] and self.font_paths['bold']:
223
- fonts['title'] = ImageFont.truetype(self.font_paths['bold'], 60)
224
- fonts['text'] = ImageFont.truetype(self.font_paths['regular'], 40)
225
- fonts['subtitle'] = ImageFont.truetype(self.font_paths['regular'], 30)
226
- else:
227
- raise ValueError("No suitable fonts found")
228
- except Exception as e:
229
- print(f"Font loading error: {e}. Using default font.")
230
- default = ImageFont.load_default()
231
- fonts = {
232
- 'title': default,
233
- 'text': default,
234
- 'subtitle': default
235
- }
236
- return fonts
237
-
238
- def generate(
239
- self,
240
- score: float,
241
- name: str,
242
- course_name: str,
243
- company_logo: Optional[str] = None,
244
- participant_photo: Optional[str] = None
245
- ) -> str:
246
- """
247
- Generate a certificate with custom styling and optional logo/photo
248
- """
249
- try:
250
- certificate = self._create_base_certificate()
251
- draw = ImageDraw.Draw(certificate)
252
-
253
- fonts = self._load_fonts()
254
- self._add_borders(draw)
255
- self._add_content(draw, fonts, str(name), str(course_name), float(score))
256
- self._add_images(certificate, company_logo, participant_photo)
257
-
258
- return self._save_certificate(certificate)
259
- except Exception as e:
260
- print(f"Error generating certificate: {e}")
261
- return None
262
-
263
- def _create_base_certificate(self) -> Image.Image:
264
- return Image.new('RGB', self.certificate_size, self.background_color)
265
-
266
- def _load_fonts(self) -> Dict[str, ImageFont.FreeTypeFont]:
267
- try:
268
- return {
269
- 'title': ImageFont.truetype("arial.ttf", 60),
270
- 'text': ImageFont.truetype("arial.ttf", 40),
271
- 'subtitle': ImageFont.truetype("arial.ttf", 30)
272
- }
273
- except Exception as e:
274
- print(f"Font loading error: {e}. Using default font.")
275
- default = ImageFont.load_default()
276
- return {'title': default, 'text': default, 'subtitle': default}
277
-
278
- def _add_borders(self, draw: ImageDraw.Draw):
279
- # Main borders
280
- draw.rectangle([20, 20, 1180, 780], outline=self.border_color, width=3)
281
- draw.rectangle([40, 40, 1160, 760], outline=self.border_color, width=1)
282
-
283
- # Decorative corners
284
- self._add_decorative_corners(draw)
285
-
286
- def _add_decorative_corners(self, draw: ImageDraw.Draw):
287
- corner_size = 20
288
- corners = [
289
- # Top-left
290
- [(20, 40), (20 + corner_size, 40)],
291
- [(40, 20), (40, 20 + corner_size)],
292
- # Top-right
293
- [(1180 - corner_size, 40), (1180, 40)],
294
- [(1160, 20), (1160, 20 + corner_size)],
295
- # Bottom-left
296
- [(20, 760), (20 + corner_size, 760)],
297
- [(40, 780 - corner_size), (40, 780)],
298
- # Bottom-right
299
- [(1180 - corner_size, 760), (1180, 760)],
300
- [(1160, 780 - corner_size), (1160, 780)]
301
- ]
302
-
303
  class CertificateGenerator:
304
  def __init__(self):
305
  self.certificate_size = (1200, 800)
306
- self.border_color = '#FFFFFF' # White background
307
  self.background_color = '#FFFFFF'
 
308
 
309
  # Install fonts if needed
310
  FontManager.install_fonts()
@@ -315,10 +219,10 @@ class CertificateGenerator:
315
  fonts = {}
316
  try:
317
  if self.font_paths['regular'] and self.font_paths['bold']:
318
- fonts['title'] = ImageFont.truetype(self.font_paths['bold'], 36) # Smaller for course name
319
- fonts['subtitle'] = ImageFont.truetype(self.font_paths['regular'], 14) # For labels
320
- fonts['text'] = ImageFont.truetype(self.font_paths['regular'], 20) # For regular text
321
- fonts['name'] = ImageFont.truetype(self.font_paths['bold'], 32) # For participant name
322
  else:
323
  raise ValueError("No suitable fonts found")
324
  except Exception as e:
@@ -344,12 +248,18 @@ class CertificateGenerator:
344
  certificate = self._create_base_certificate()
345
  draw = ImageDraw.Draw(certificate)
346
 
 
 
 
347
  fonts = self._load_fonts()
348
  self._add_content(draw, fonts, str(name), str(course_name), float(score))
349
 
350
  if company_logo:
351
  self._add_logo(certificate, company_logo)
352
 
 
 
 
353
  return self._save_certificate(certificate)
354
  except Exception as e:
355
  print(f"Error generating certificate: {e}")
@@ -358,6 +268,33 @@ class CertificateGenerator:
358
  def _create_base_certificate(self) -> Image.Image:
359
  return Image.new('RGB', self.certificate_size, self.background_color)
360
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
361
  def _add_content(
362
  self,
363
  draw: ImageDraw.Draw,
@@ -368,53 +305,95 @@ class CertificateGenerator:
368
  ):
369
  # Add certificate ID at top right
370
  certificate_id = f"Certificate no: {datetime.now().strftime('%Y%m%d')}-{abs(hash(name)) % 10000:04d}"
371
- draw.text((1100, 40), certificate_id, font=fonts['subtitle'], fill='#666666', anchor="ra")
372
 
373
  # Add "CERTIFICATE OF COMPLETION" text
374
- draw.text((60, 120), "CERTIFICATE OF COMPLETION", font=fonts['subtitle'], fill='#666666')
375
 
376
  # Add course name (large and bold)
377
  course_name = course_name.strip() or "Assessment"
378
- draw.multiline_text((60, 180), course_name, font=fonts['title'], fill='#1C1D1F', spacing=10)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
379
 
380
  # Add instructor info
381
- draw.text((60, 280), "Instructor", font=fonts['subtitle'], fill='#666666')
382
- draw.text((60, 310), "CertifyMe AI", font=fonts['text'], fill='#1C1D1F')
383
 
384
  # Add participant name (large)
385
  name = name.strip() or "Participant"
386
- draw.text((60, 400), name, font=fonts['name'], fill='#1C1D1F')
387
 
388
  # Add date and score info with spacing
389
  date_str = datetime.now().strftime("%b. %d, %Y")
390
 
391
  # Date section
392
- draw.text((60, 480), "Date", font=fonts['subtitle'], fill='#666666')
393
- draw.text((60, 510), date_str, font=fonts['text'], fill='#1C1D1F')
394
 
395
  # Score section
396
- draw.text((300, 480), "Score", font=fonts['subtitle'], fill='#666666')
397
- draw.text((300, 510), f"{float(score):.1f}%", font=fonts['text'], fill='#1C1D1F')
398
 
399
  # Add reference number at bottom
400
  ref_number = f"Reference Number: {abs(hash(name + date_str)) % 10000:04d}"
401
- draw.text((60, 700), ref_number, font=fonts['subtitle'], fill='#666666')
402
 
403
  def _add_logo(self, certificate: Image.Image, logo_path: str):
404
  try:
405
  logo = Image.open(logo_path)
406
  # Resize logo to appropriate size
407
  logo.thumbnail((150, 80))
408
- # Position in top-left corner
409
- certificate.paste(logo, (50, 30))
410
  except Exception as e:
411
  print(f"Error adding logo: {e}")
412
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
413
  def _save_certificate(self, certificate: Image.Image) -> str:
414
  temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.png')
415
  certificate.save(temp_file.name, 'PNG', quality=95)
416
  return temp_file.name
417
-
418
 
419
  class QuizApp:
420
  def __init__(self, api_key: str):
 
204
  """Exception raised for errors in quiz generation"""
205
  pass
206
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
207
  class CertificateGenerator:
208
  def __init__(self):
209
  self.certificate_size = (1200, 800)
 
210
  self.background_color = '#FFFFFF'
211
+ self.border_color = '#1C1D1F'
212
 
213
  # Install fonts if needed
214
  FontManager.install_fonts()
 
219
  fonts = {}
220
  try:
221
  if self.font_paths['regular'] and self.font_paths['bold']:
222
+ fonts['title'] = ImageFont.truetype(self.font_paths['bold'], 36)
223
+ fonts['subtitle'] = ImageFont.truetype(self.font_paths['regular'], 14)
224
+ fonts['text'] = ImageFont.truetype(self.font_paths['regular'], 20)
225
+ fonts['name'] = ImageFont.truetype(self.font_paths['bold'], 32)
226
  else:
227
  raise ValueError("No suitable fonts found")
228
  except Exception as e:
 
248
  certificate = self._create_base_certificate()
249
  draw = ImageDraw.Draw(certificate)
250
 
251
+ # Add professional border
252
+ self._add_professional_border(draw)
253
+
254
  fonts = self._load_fonts()
255
  self._add_content(draw, fonts, str(name), str(course_name), float(score))
256
 
257
  if company_logo:
258
  self._add_logo(certificate, company_logo)
259
 
260
+ if participant_photo:
261
+ self._add_photo(certificate, participant_photo)
262
+
263
  return self._save_certificate(certificate)
264
  except Exception as e:
265
  print(f"Error generating certificate: {e}")
 
268
  def _create_base_certificate(self) -> Image.Image:
269
  return Image.new('RGB', self.certificate_size, self.background_color)
270
 
271
+ def _add_professional_border(self, draw: ImageDraw.Draw):
272
+ # Outer border
273
+ draw.rectangle(
274
+ [(20, 20), (self.certificate_size[0] - 20, self.certificate_size[1] - 20)],
275
+ outline='#1C1D1F',
276
+ width=2
277
+ )
278
+
279
+ # Inner border
280
+ draw.rectangle(
281
+ [(40, 40), (self.certificate_size[0] - 40, self.certificate_size[1] - 40)],
282
+ outline='#1C1D1F',
283
+ width=1
284
+ )
285
+
286
+ # Corner decorations
287
+ corner_size = 20
288
+ for x, y in [(20, 20), (self.certificate_size[0] - 20, 20),
289
+ (20, self.certificate_size[1] - 20),
290
+ (self.certificate_size[0] - 20, self.certificate_size[1] - 20)]:
291
+ draw.rectangle(
292
+ [(x - corner_size, y - corner_size),
293
+ (x + corner_size, y + corner_size)],
294
+ outline='#1C1D1F',
295
+ width=1
296
+ )
297
+
298
  def _add_content(
299
  self,
300
  draw: ImageDraw.Draw,
 
305
  ):
306
  # Add certificate ID at top right
307
  certificate_id = f"Certificate no: {datetime.now().strftime('%Y%m%d')}-{abs(hash(name)) % 10000:04d}"
308
+ draw.text((1100, 60), certificate_id, font=fonts['subtitle'], fill='#666666', anchor="ra")
309
 
310
  # Add "CERTIFICATE OF COMPLETION" text
311
+ draw.text((60, 140), "CERTIFICATE OF COMPLETION", font=fonts['subtitle'], fill='#666666')
312
 
313
  # Add course name (large and bold)
314
  course_name = course_name.strip() or "Assessment"
315
+ text_width = draw.textlength(course_name, fonts['title'])
316
+ max_width = self.certificate_size[0] - 120 # Leave margins
317
+ if text_width > max_width:
318
+ # Split into multiple lines if too long
319
+ words = course_name.split()
320
+ lines = []
321
+ current_line = []
322
+ current_width = 0
323
+ for word in words:
324
+ word_width = draw.textlength(word + " ", fonts['title'])
325
+ if current_width + word_width <= max_width:
326
+ current_line.append(word)
327
+ current_width += word_width
328
+ else:
329
+ lines.append(" ".join(current_line))
330
+ current_line = [word]
331
+ current_width = word_width
332
+ if current_line:
333
+ lines.append(" ".join(current_line))
334
+ course_name = "\n".join(lines)
335
+
336
+ draw.multiline_text((60, 200), course_name, font=fonts['title'], fill='#1C1D1F', spacing=10)
337
 
338
  # Add instructor info
339
+ draw.text((60, 300), "Instructor", font=fonts['subtitle'], fill='#666666')
340
+ draw.text((60, 330), "CertifyMe AI", font=fonts['text'], fill='#1C1D1F')
341
 
342
  # Add participant name (large)
343
  name = name.strip() or "Participant"
344
+ draw.text((60, 420), name, font=fonts['name'], fill='#1C1D1F')
345
 
346
  # Add date and score info with spacing
347
  date_str = datetime.now().strftime("%b. %d, %Y")
348
 
349
  # Date section
350
+ draw.text((60, 500), "Date", font=fonts['subtitle'], fill='#666666')
351
+ draw.text((60, 530), date_str, font=fonts['text'], fill='#1C1D1F')
352
 
353
  # Score section
354
+ draw.text((300, 500), "Score", font=fonts['subtitle'], fill='#666666')
355
+ draw.text((300, 530), f"{float(score):.1f}%", font=fonts['text'], fill='#1C1D1F')
356
 
357
  # Add reference number at bottom
358
  ref_number = f"Reference Number: {abs(hash(name + date_str)) % 10000:04d}"
359
+ draw.text((60, 720), ref_number, font=fonts['subtitle'], fill='#666666')
360
 
361
  def _add_logo(self, certificate: Image.Image, logo_path: str):
362
  try:
363
  logo = Image.open(logo_path)
364
  # Resize logo to appropriate size
365
  logo.thumbnail((150, 80))
366
+ # Position in top-left corner with padding
367
+ certificate.paste(logo, (60, 50), mask=logo if 'A' in logo.getbands() else None)
368
  except Exception as e:
369
  print(f"Error adding logo: {e}")
370
 
371
+ def _add_photo(self, certificate: Image.Image, photo_path: str):
372
+ try:
373
+ photo = Image.open(photo_path)
374
+ # Create circular mask
375
+ size = (100, 100)
376
+ mask = Image.new('L', size, 0)
377
+ draw = ImageDraw.Draw(mask)
378
+ draw.ellipse((0, 0, size[0], size[1]), fill=255)
379
+
380
+ # Resize photo maintaining aspect ratio
381
+ photo.thumbnail(size)
382
+
383
+ # Create a circular photo
384
+ output = Image.new('RGBA', size, (0, 0, 0, 0))
385
+ output.paste(photo, (0, 0))
386
+ output.putalpha(mask)
387
+
388
+ # Position in top-right corner with padding
389
+ certificate.paste(output, (1000, 50), mask=output)
390
+ except Exception as e:
391
+ print(f"Error adding photo: {e}")
392
+
393
  def _save_certificate(self, certificate: Image.Image) -> str:
394
  temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.png')
395
  certificate.save(temp_file.name, 'PNG', quality=95)
396
  return temp_file.name
 
397
 
398
  class QuizApp:
399
  def __init__(self, api_key: str):