📄 Certificate of Completion

Certificate of Completion 

Click Here to go to the generator 


Here is the code used if needed in the future. This is also on Joseph's paid Google account 

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Medart University - Certificate Generator (Canvas)</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js" xintegrity="sha512-GsLlZN/3F2ErC5ifS5QtgpiJtWd43JWSuIgh7mbzZ8zBps+dvLusV+eNQATqgA/HdeKFVgA5v3S/cIrLF7QnIg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<!-- Load Google Fonts for a professional look -->
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&family=Playfair+Display:wght@700&display=swap" rel="stylesheet">
<style>
/* Base font for the body */
body {
font-family: 'Inter', sans-serif;
}
/* Styling for the canvas element, making it visually distinct for the preview */
canvas {
border: 10px solid #1e3a8a; /* A deep blue color for the outer border */
background-color: #f0f4f8; /* A light, textured background */
width: 1000px; /* Actual drawing width for the canvas internal rendering */
height: 750px; /* Adjusted height to prevent cutting off */
/* The transform is for the on-screen preview only, scaling it down to fit */
transform: scale(0.8);
transform-origin: top center;
margin-top: 2rem;
margin-bottom: 2rem;
box-sizing: content-box; /* Ensures padding and border are included in total width/height */
}
/* Hidden element to ensure 'Playfair Display' font is loaded by the browser before drawing on canvas */
.font-preload {
font-family: 'Playfair Display', serif;
position: absolute;
left: -9999px; /* Move off-screen */
visibility: hidden;
pointer-events: none;
}
</style>
</head>
<body class="bg-gray-100 flex flex-col items-center justify-center min-h-screen p-4">

<!-- Form for generating the certificate details -->
<div class="w-full max-w-lg bg-white p-8 rounded-lg shadow-lg" id="form-container">
<h1 class="text-3xl font-bold text-center text-gray-800 mb-6">Certificate Generator (Canvas)</h1>
<p class="text-center text-gray-600 mb-8">Fill in the details below to generate the certificate.</p>

<div class="space-y-6">
<div>
<label for="studentName" class="block text-sm font-medium text-gray-700">Student's Full Name</label>
<input type="text" id="studentName" name="studentName" placeholder="e.g., Jane Doe" class="mt-1 block w-full px-4 py-2 bg-white border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500">
</div>
<div>
<label for="courseName" class="block text-sm font-medium text-gray-700">Education Level / Course</label>
<input type="text" id="courseName" name="courseName" placeholder="e.g., Bachelor of Science in Computer Science" class="mt-1 block w-full px-4 py-2 bg-white border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500">
</div>
<div>
<label for="imageUrl" class="block text-sm font-medium text-gray-700">Certificate Image URL (Optional)</label>
<input type="text" id="imageUrl" name="imageUrl" value="https://static.wixstatic.com/media/7d136f_4cd5ec277c45402a98373beccabbbd7a~mv2.png/v1/fill/w_390,h_78,al_c,q_95,usm_0.66_1.00_0.01,enc_avif,quality_auto/7d136f_4cd5ec277c45402a98373beccabbbd7a~mv2.png" class="mt-1 block w-full px-4 py-2 bg-white border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500">
</div>
<div>
<label for="completionDate" class="block text-sm font-medium text-gray-700">Date of Completion</label>
<input type="date" id="completionDate" name="completionDate" class="mt-1 block w-full px-4 py-2 bg-white border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500">
</div>
</div>

<div class="mt-8">
<button id="printButton" class="w-full flex justify-center py-3 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
Generate and Save PDF
</button>
</div>
</div>

<!-- Certificate Preview Area - now a canvas element -->
<canvas id="certificateCanvas" width="1000" height="750"></canvas> <!-- Height adjusted -->

<!-- Hidden element to ensure 'Playfair Display' font is loaded by the browser -->
<div class="font-preload">.</div>

<script>
// Get references to DOM elements
const studentNameInput = document.getElementById('studentName');
const courseNameInput = document.getElementById('courseName');
const completionDateInput = document.getElementById('completionDate');
const imageUrlInput = document.getElementById('imageUrl');
const printButton = document.getElementById('printButton');
const certificateCanvas = document.getElementById('certificateCanvas');
const ctx = certificateCanvas.getContext('2d'); // Get the 2D rendering context

// Define the fixed dimensions for the canvas, matching the desired certificate size
const CERT_WIDTH = 1000;
const CERT_HEIGHT = 750; // Adjusted height

/**
* Draws the entire certificate onto the canvas.
* This function is asynchronous because it needs to wait for images to load.
* @returns {Promise<void>} A promise that resolves when all drawing is complete.
*/
async function drawCertificate() {
return new Promise(resolve => {
// Clear the entire canvas before redrawing
ctx.clearRect(0, 0, CERT_WIDTH, CERT_HEIGHT);

// Draw the background color for the certificate
ctx.fillStyle = '#f0f4f8'; /* Light, textured background */
ctx.fillRect(0, 0, CERT_WIDTH, CERT_HEIGHT);

// Draw the inner border of the certificate
ctx.strokeStyle = '#a0aec0'; /* Lighter silver/gray border */
ctx.lineWidth = 2;
// Border extends to the new height
ctx.strokeRect(30, 30, CERT_WIDTH - 60, CERT_HEIGHT - 60);

// Retrieve values from the input fields
const studentName = studentNameInput.value.trim() || 'Student Name';
const courseName = courseNameInput.value.trim() || 'Education Level / Course';
const imageUrl = imageUrlInput.value.trim();
const completionDate = completionDateInput.value;

let formattedDate = 'Completion Date';
if (completionDate) {
const date = new Date(completionDate + 'T00:00:00');
const options = { year: 'numeric', month: 'long', day: 'numeric' };
formattedDate = date.toLocaleDateString('en-US', options);
}

// --- Header Section ---
// Draw a simple geometric university icon
ctx.fillStyle = '#1e3a8a'; /* Deep blue */
// Left triangle
ctx.beginPath();
ctx.moveTo(CERT_WIDTH / 2 - 80, 100);
ctx.lineTo(CERT_WIDTH / 2 - 60, 140);
ctx.lineTo(CERT_WIDTH / 2 - 100, 140);
ctx.closePath();
ctx.fill();

// Right triangle
ctx.beginPath();
ctx.moveTo(CERT_WIDTH / 2 + 80, 100);
ctx.lineTo(CERT_WIDTH / 2 + 60, 140);
ctx.lineTo(CERT_WIDTH / 2 + 100, 140);
ctx.closePath();
ctx.fill();

// Center circle
ctx.beginPath();
ctx.arc(CERT_WIDTH / 2, 110, 20, 0, Math.PI * 2);
ctx.fill();

// University Name Text
ctx.fillStyle = '#1e3a8a'; /* Deep blue */
ctx.font = '700 48px "Playfair Display", serif'; /* Bold, larger font for main title */
ctx.textAlign = 'center'; /* Center align text horizontally */
ctx.fillText('MEDART UNIVERSITY', CERT_WIDTH / 2, 180);

// Certificate of Completion subheading
ctx.fillStyle = '#374151'; /* Gray-800 */
ctx.font = '400 24px "Inter", sans-serif';
ctx.fillText('Certificate of Completion', CERT_WIDTH / 2, 220);

// --- Certificate Image Section ---
let imageFinishedLoading = true; // Flag to track image loading state

if (imageUrl) {
imageFinishedLoading = false; // Set to false if an image URL is provided
const img = new Image();
img.crossOrigin = 'anonymous'; // Essential for loading images from other domains to avoid CORS issues and allow canvas to read them
img.onload = () => {
const imgWidth = Math.min(img.width, 250); // Constrain image width
const imgHeight = (img.height / img.width) * imgWidth; // Maintain aspect ratio
const imgX = (CERT_WIDTH - imgWidth) / 2; // Center image horizontally
const imgY = 250; // Vertical position below the title

// Draw the image onto the canvas
ctx.drawImage(img, imgX, imgY, imgWidth, imgHeight);
imageFinishedLoading = true;
resolve(); // Resolve the promise once the image is successfully drawn
};
img.onerror = () => {
console.error('Image failed to load:', imageUrl);
// Draw a placeholder rectangle and text if the image fails to load
ctx.fillStyle = '#cccccc';
ctx.fillRect((CERT_WIDTH - 250) / 2, 250, 250, 100); /* Placeholder box */
ctx.fillStyle = '#6b7280';
ctx.font = '400 16px "Inter", sans-serif';
ctx.fillText('Image Not Found', CERT_WIDTH / 2, 300);
imageFinishedLoading = true;
resolve(); // Resolve the promise even if the image fails
};
img.src = imageUrl; // Set the image source to start loading
}

// --- Main Certificate Content ---
// "This is to certify that" text
ctx.fillStyle = '#374151'; /* Gray-800 */
ctx.font = '400 20px "Inter", sans-serif';
ctx.fillText('This is to certify that', CERT_WIDTH / 2, 400);

// Student's Name
ctx.fillStyle = '#1f2937'; /* Gray-900 */
ctx.font = '700 40px "Playfair Display", serif';
ctx.fillText(studentName, CERT_WIDTH / 2, 450);

// Draw an underline for the student's name
const textMetrics = ctx.measureText(studentName);
const underlineY = 455;
const underlineWidth = textMetrics.width + 40; /* Add some padding around the text */
ctx.strokeStyle = '#6b7280'; /* Gray-500 */
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo((CERT_WIDTH - underlineWidth) / 2, underlineY);
ctx.lineTo((CERT_WIDTH + underlineWidth) / 2, underlineY);
ctx.stroke();

// "has successfully completed the requirements for" text
ctx.fillStyle = '#374151'; /* Gray-800 */
ctx.font = '400 20px "Inter", sans-serif';
ctx.fillText('has successfully completed the requirements for', CERT_WIDTH / 2, 500);

// Course Name
ctx.fillStyle = '#1f2937'; /* Gray-900 */
ctx.font = '700 32px "Playfair Display", serif';
ctx.fillText(courseName, CERT_WIDTH / 2, 540);

// --- Footer Section (Date and Signature) ---
// Adjust footer position relative to new canvas height
const footerTextY = CERT_HEIGHT - 120; // Position higher from the new bottom edge
const underlineOffset = 10; // Offset from text baseline for the underline
const labelOffset = 30; // Offset from text baseline for the labels ("Date", "University President")

ctx.fillStyle = '#374151'; /* Gray-800 */
ctx.font = '400 16px "Inter", sans-serif';
ctx.textAlign = 'center';

// Date information
ctx.fillText(formattedDate, CERT_WIDTH / 2 - 200, footerTextY);
ctx.strokeStyle = '#6b7280';
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(CERT_WIDTH / 2 - 300, footerTextY + underlineOffset); // Underline below text
ctx.lineTo(CERT_WIDTH / 2 - 100, footerTextY + underlineOffset);
ctx.stroke();
ctx.font = '600 14px "Inter", sans-serif';
ctx.fillText('Date', CERT_WIDTH / 2 - 200, footerTextY + labelOffset); // Label below underline

// Signature line and title
ctx.font = 'italic 400 28px "Playfair Display", serif'; /* More cursive/signature-like font */
ctx.fillText('Brian Jones', CERT_WIDTH / 2 + 200, footerTextY);
ctx.strokeStyle = '#6b7280';
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(CERT_WIDTH / 2 + 100, footerTextY + underlineOffset); // Underline below text
ctx.lineTo(CERT_WIDTH / 2 + 300, footerTextY + underlineOffset);
ctx.stroke();
ctx.font = '600 14px "Inter", sans-serif';
ctx.fillText('University President', CERT_WIDTH / 2 + 200, footerTextY + labelOffset); // Label below underline

// If no image URL was provided, resolve the promise immediately after drawing static content
if (imageFinishedLoading) {
resolve();
}
});
}

/**
* Sets the default completion date to today's date.
*/
function setDefaultDate() {
const today = new Date();
const yyyy = today.getFullYear();
const mm = String(today.getMonth() + 1).padStart(2, '0'); // Month is 0-indexed
const dd = String(today.getDate()).padStart(2, '0');
completionDateInput.value = `${yyyy}-${mm}-${dd}`;
}

// --- Event Listeners and Initial Setup ---
window.onload = () => {
setDefaultDate(); // Set today's date as default
drawCertificate(); // Perform initial drawing of the certificate

// Add event listeners to input fields to redraw the certificate on changes
studentNameInput.addEventListener('input', drawCertificate);
courseNameInput.addEventListener('input', drawCertificate);
completionDateInput.addEventListener('change', drawCertificate);
imageUrlInput.addEventListener('input', drawCertificate); // Image loading is handled within drawCertificate
};

// Event listener for the "Generate and Save PDF" button
printButton.addEventListener('click', async () => {
const studentName = studentNameInput.value.trim() || 'student';
const fileName = `Certificate-${studentName.replace(/\s+/g, '_')}.pdf`;

// Disable the button and update its text to provide feedback
printButton.disabled = true;
printButton.textContent = 'Generating PDF...';

// Ensure the canvas is fully drawn (especially if an image is still loading) before generating PDF
await drawCertificate();

// Options for html2pdf.js
const opt = {
margin: 0, /* No margins */
filename: fileName,
image: { type: 'jpeg', quality: 1.0 }, /* Output image as JPEG with highest quality */
html2canvas: { scale: 4, useCORS: true, letterRendering: true, width: CERT_WIDTH, height: CERT_HEIGHT }, /* Render canvas at 4x scale for sharpness */
jsPDF: { unit: 'px', format: [CERT_WIDTH, CERT_HEIGHT], orientation: 'landscape' } /* PDF unit and format matching canvas dimensions */
};

// Generate and save the PDF from the canvas
html2pdf().from(certificateCanvas).set(opt).save().finally(() => {
// Re-enable the button and reset its text after PDF generation is complete
printButton.disabled = false;
printButton.textContent = 'Generate and Save PDF';
});
});
</script>
</body>
</html>