You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(
originalImg,
fullName = 'DOE, JANE',
dob = '05/20/1990',
issueDate = '05/20/2023',
expiresDate = '05/20/2031',
address = '123 ANYTOWN ST\nANYTOWN, AZ 85007',
idNumber = 'Y12345678',
sex = 'F',
height = '5-05',
eyes = 'BRO',
hair = 'BLK',
restrictions = 'B',
endorsements = 'NONE',
donor = 'ORGAN DONOR',
classType = 'D',
signatureText = 'Jane Doe'
) {
// --- Font Loading ---
// Dynamically create a <style> tag to import Google Fonts
const fontUrl = 'https://fonts.googleapis.com/css2?family=Oswald:wght@400;700&family=Roboto:wght@400;500;700&family=Dancing+Script:wght@700&display=swap';
if (!document.querySelector(`link[href="${fontUrl}"]`)) {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = fontUrl;
document.head.appendChild(link);
// A short delay to allow the link tag to be processed, then wait for fonts.
await new Promise(resolve => setTimeout(resolve, 100));
}
// Wait for the fonts to be ready
try {
await document.fonts.load('700 36px Oswald');
await document.fonts.load('400 16px Roboto');
await document.fonts.load('700 30px "Dancing Script"');
} catch (e) {
console.error("Font loading failed:", e);
}
// --- Canvas Setup ---
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const canvasWidth = 856;
const canvasHeight = 540;
canvas.width = canvasWidth;
canvas.height = canvasHeight;
ctx.imageSmoothingEnabled = true;
ctx.imageSmoothingQuality = 'high';
// --- Draw Background ---
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
// Sky gradient
const skyGradient = ctx.createLinearGradient(0, 60, 0, 350);
skyGradient.addColorStop(0, '#8cb1de');
skyGradient.addColorStop(0.7, '#fcd2a0');
skyGradient.addColorStop(1, '#fde6b2');
ctx.fillStyle = skyGradient;
ctx.fillRect(0, 60, canvasWidth, 290);
// Sun
ctx.fillStyle = 'rgba(255, 255, 220, 0.9)';
ctx.beginPath();
ctx.arc(600, 140, 25, 0, Math.PI * 2);
ctx.fill();
// Landscape silhouette
ctx.fillStyle = '#7a3e14';
ctx.beginPath();
ctx.moveTo(0, canvasHeight);
ctx.lineTo(0, 310);
ctx.bezierCurveTo(50, 280, 150, 350, 250, 330);
ctx.lineTo(300, 360);
ctx.bezierCurveTo(400, 380, 450, 340, 520, 350);
ctx.lineTo(600, 300);
ctx.bezierCurveTo(700, 250, 800, 340, canvasWidth, 320);
ctx.lineTo(canvasWidth, canvasHeight);
ctx.closePath();
ctx.fill();
ctx.fillStyle = '#a16e4b';
ctx.beginPath();
ctx.moveTo(0, canvasHeight);
ctx.lineTo(0, 350);
ctx.bezierCurveTo(100, 340, 200, 380, 300, 370);
ctx.lineTo(350, 390);
ctx.bezierCurveTo(450, 420, 550, 360, 650, 380);
ctx.lineTo(750, 350);
ctx.bezierCurveTo(800, 340, canvasWidth, 360, canvasWidth, 360);
ctx.lineTo(canvasWidth, canvasHeight);
ctx.closePath();
ctx.fill();
// "ARIZONA" watermark
ctx.save();
ctx.font = 'bold 150px Oswald';
ctx.fillStyle = 'rgba(255, 255, 255, 0.15)';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('ARIZONA', canvasWidth / 2 + 50, canvasHeight / 2 + 30);
ctx.restore();
// --- Draw Header ---
ctx.fillStyle = '#1c2e54'; // Dark blue
ctx.fillRect(0, 0, canvasWidth, 65);
// Arizona state outline/seal simplified
ctx.fillStyle = '#e4a133'; // Gold color
const arizonaPath = new Path2D("M 43.6,13.5 L 96.9,13.5 L 96.9,13.5 L 96.9,21 L 79.9,50.7 L 43.6,50.7 L 43.6,13.5 Z");
ctx.fill(arizonaPath);
ctx.fillStyle = '#1c2e54';
ctx.save();
ctx.clip(arizonaPath);
ctx.beginPath();
ctx.arc(70, 32, 7, 0, 2 * Math.PI);
ctx.fillStyle = '#b7d4ea';
ctx.fill();
ctx.restore();
ctx.fillStyle = 'white';
ctx.font = 'bold 36px Oswald';
ctx.textAlign = 'left';
ctx.textBaseline = 'middle';
ctx.fillText('ARIZONA', 110, 38);
ctx.font = 'bold 18px Roboto';
ctx.textAlign = 'right';
ctx.fillText('DRIVER LICENSE', canvasWidth - 20, 25);
ctx.font = 'bold 16px Roboto';
ctx.fillText(`CLASS ${classType}`, canvasWidth - 20, 50);
// --- Draw Photos ---
ctx.drawImage(originalImg, 25, 80, 220, 280);
ctx.strokeStyle = '#333';
ctx.lineWidth = 1;
ctx.strokeRect(25, 80, 220, 280);
// Ghost Photo
ctx.save();
ctx.globalAlpha = 0.3;
ctx.drawImage(originalImg, 290, 190, 130, 165);
ctx.restore();
// --- Draw Text Data ---
const dataStartX = 270;
// ID Number (red) and Expiration
ctx.fillStyle = '#b30000'; // Red
ctx.font = 'bold 20px Roboto';
ctx.fillText(`1 DL ${idNumber}`, dataStartX, 100);
ctx.fillText(`4b EXP ${expiresDate}`, dataStartX, 125);
// Name
ctx.fillStyle = '#1c2e54';
ctx.font = 'bold 26px Oswald';
const nameParts = fullName.split(',');
ctx.fillText(`LAST ${nameParts[0]}`, dataStartX, 160);
ctx.fillText(`FIRST ${nameParts[1] || ''}`.trim(), dataStartX, 185);
// Address
ctx.font = '16px Roboto';
ctx.fillStyle = 'black';
const addressLines = address.split('\n');
addressLines.forEach((line, index) => {
ctx.fillText(line, dataStartX, 225 + (index * 20));
});
// Details grid
const detailYStart = 300;
const detailCol1 = dataStartX;
const detailCol2 = dataStartX + 120;
const detailCol3 = dataStartX + 240;
const drawDetail = (label, value, x, y) => {
ctx.fillStyle = '#555';
ctx.font = 'bold 14px Roboto';
ctx.fillText(label, x, y);
ctx.fillStyle = 'black';
ctx.font = '16px Roboto';
ctx.fillText(value, x, y + 18);
}
drawDetail('3 DOB', dob, detailCol1, detailYStart);
drawDetail('4d SEX', sex, detailCol2, detailYStart);
drawDetail('HGT', height, detailCol3, detailYStart);
drawDetail('EYES', eyes, detailCol1, detailYStart + 50);
drawDetail('HAIR', hair, detailCol2, detailYStart + 50);
drawDetail('4a ISSUE', issueDate, detailCol3, detailYStart + 50);
// Under 21 vertical bar
const dobDate = new Date(dob);
const issueDateObj = new Date(issueDate);
const ageAtIssue = (issueDateObj - dobDate) / (1000 * 60 * 60 * 24 * 365.25);
if (ageAtIssue < 21) {
const turns21 = new Date(dobDate);
turns21.setFullYear(dobDate.getFullYear() + 21);
const turns21String = `${(turns21.getMonth() + 1).toString().padStart(2, '0')}-${turns21.getDate().toString().padStart(2, '0')}-${turns21.getFullYear()}`;
ctx.fillStyle = '#b30000';
ctx.fillRect(canvasWidth - 45, 70, 40, 200);
ctx.save();
ctx.translate(canvasWidth - 25, 170);
ctx.rotate(-Math.PI / 2);
ctx.fillStyle = 'white';
ctx.font = 'bold 16px Roboto';
ctx.textAlign = 'center';
ctx.fillText(`UNDER 21 UNTIL ${turns21String}`, 0, 0);
ctx.restore();
}
// --- Bottom Section ---
if (donor) {
ctx.fillStyle = '#D80027';
ctx.font = '24px sans-serif';
ctx.fillText('♥', 25, 385);
ctx.fillStyle = 'black';
ctx.font = '14px Roboto';
ctx.fillText(donor, 55, 385);
}
// Signature
ctx.strokeStyle = '#555';
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(25, 480);
ctx.lineTo(245, 480);
ctx.stroke();
ctx.fillStyle = '#1c2e54';
ctx.font = '700 30px "Dancing Script"';
ctx.fillText(signatureText, 30, 475);
ctx.font = 'bold 12px Roboto';
ctx.fillStyle = 'black';
ctx.fillText("SIGNATURE", 25, 495);
// Fake 2D barcode (PDF417)
ctx.fillStyle = 'black';
for (let y = 390; y < 510; y += 5) {
for (let x = 270; x < 550; x += 3) {
if (Math.random() > 0.4) {
ctx.fillRect(x, y, Math.random() * 5 + 1, 4);
}
}
}
// Fake 1D barcode
for (let i = 0; i < 80; i++) {
const x = 580 + i * 3;
if (Math.random() > 0.3) {
ctx.fillRect(x, 420, Math.random() > 0.5 ? 1 : 2, 80);
}
}
ctx.font = '14px Roboto';
ctx.fillText(`REST: ${restrictions}`, 580, 515);
ctx.fillText(`END: ${endorsements}`, 700, 515);
return canvas;
}
Apply Changes