You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, licenseNumber = 'M123-456-789-012', lastName = 'SAMPLER', firstName = 'AVA', middleName = 'JO', address = '123 MAIN STREET', cityStateZip = 'SAINT PAUL, MN 55101', dob = '08-15-1995', sex = 'F', height = '5\'-05"', eyes = 'BLU', issueDate = '08-15-2021', expiresDate = '08-15-2025', signatureText = 'Ava Jo Sampler') {
// Helper function to load Google Fonts. It checks if the font is already loaded.
const loadFont = async (fontName, fontUrl) => {
if (!document.fonts.check(`1em "${fontName}"`)) {
const fontFace = new FontFace(fontName, `url(${fontUrl})`);
try {
await fontFace.load();
document.fonts.add(fontFace);
} catch (e) {
console.error(`Font ${fontName} could not be loaded:`, e);
}
}
};
// Load a cursive font for the signature to make it look realistic.
await loadFont('Dancing Script', 'https://fonts.gstatic.com/s/dancingscript/v24/If2RXTr6YS-zF4S-kcSWSVi_szLviuEViw.woff2');
const canvas = document.createElement('canvas');
// Standard ID card aspect ratio (85.6mm x 53.98mm) at a high resolution for clarity.
const W = 1024;
const H = 642;
canvas.width = W;
canvas.height = H;
const ctx = canvas.getContext('2d');
// --- Draw Background Scene ---
// Sky gradient
const sky = ctx.createLinearGradient(0, 0, 0, H);
sky.addColorStop(0, '#75b3e2');
sky.addColorStop(0.5, '#a1d0f0');
sky.addColorStop(1, '#d5eaf9');
ctx.fillStyle = sky;
ctx.fillRect(0, 0, W, H);
// Lake
ctx.fillStyle = '#4c7aa1';
ctx.beginPath();
ctx.moveTo(0, H * 0.55);
ctx.bezierCurveTo(W * 0.25, H * 0.5, W * 0.75, H * 0.6, W, H * 0.55);
ctx.lineTo(W, H);
ctx.lineTo(0, H);
ctx.closePath();
ctx.fill();
// Distant treeline
ctx.fillStyle = '#2a4d3e';
for (let i = 0; i < 80; i++) {
const x = Math.random() * W;
const y = H * 0.55 - Math.random() * 20;
const h = 20 + Math.random() * 30;
ctx.beginPath();
ctx.moveTo(x, y);
ctx.lineTo(x + 5, y - h);
ctx.lineTo(x + 10, y);
ctx.closePath();
ctx.fill();
}
// Draw Minnesota state outline faintly in the background for security texture
ctx.save();
ctx.globalAlpha = 0.08;
ctx.strokeStyle = '#FFFFFF';
ctx.lineWidth = 15;
// SVG path data for the state of Minnesota
const mnPath = new Path2D("M615.8,327.1l-42.5-0.3l-0.3,101.4l-11.3,0.3l-0.3-101.4l-20.7,0.3v-54.8l-12.7-0.3v-17.7l-9.1,0.3v-29.4l4.8-0.3v-9.1l-10-0.2v-10.7l-15.1,0.2v-10.7l-16,0.3l0.3,16.2l-10,0.3l0.3-21.2l-12.5-0.2l-0.2,25.7l-10.7-0.5l-0.2-22l-10.7,0.5l-0.3-12.7l-14.4-1.1l-0.3,11.3l-10-0.3l-0.5-23.4l-7.3,0.3v-10l-15.1-0.3v-10l-9.3,0.3v-21.2l-9.3,0.3v-12.5l-25.5,0.3v-17.7l-24.5-0.3V98.4l-20.2,0.3l-0.3,55.8l-15.2,0.3v6.8l-9.1-0.3l-0.2,16.2l-5.3-0.5l-0.3,12l-5.8,1.1l-1.3,71.5h8.8l0.5,39.3l-8.3,0.5l0.5,33l-10.7,0.5l0.3,31.6l-14.2,0.2l-0.5-29.4l-10-0.3l-0.2-26l-7.6-0.5l-0.2,21.5l14.4,0.5l-0.2,29.9l-22.9,0.3l-0.2,16.2l14.2,0.3l0.2,46.1l11.8,0.3l-0.5-16.5l8.5-0.2l0.2-16.2l10,0.3l-0.2,16l14.4,0.3l-0.2,16.5l8.3,0.5l6.8,40.9l30.1,0.3l0.5-29.4h8.5l-0.5,29.4h22.9l-0.5-29.4h8l-0.3,29.4l15.4-0.3l0.3-22.9l8.8-0.5l-0.5,31.8l20.5-0.2l0.3-29.2l12.7-0.3l-0.2,29.4h20.5l0.2-29.4h11.3l-0.5,29.4h22.7l0.5-49.4l11.3-0.3l-0.2,69.5l49,0.3l1.8-316.3L615.8,327.1z");
ctx.scale(1.5, 1.5);
ctx.translate(-150, -150);
ctx.stroke(mnPath);
ctx.restore();
// Draw loon silhouette on the lake
ctx.fillStyle = '#1a1a1a';
ctx.beginPath(); // Body
ctx.moveTo(W * 0.65, H * 0.62);
ctx.bezierCurveTo(W * 0.6, H * 0.58, W * 0.5, H * 0.6, W * 0.45, H * 0.65);
ctx.bezierCurveTo(W * 0.4, H * 0.7, W * 0.48, H * 0.75, W * 0.6, H * 0.7);
ctx.bezierCurveTo(W * 0.7, H * 0.68, W * 0.72, H * 0.65, W * 0.65, H * 0.62);
ctx.closePath();
ctx.fill();
ctx.beginPath(); // Head & Beak
ctx.arc(W * 0.45, H * 0.64, 15, Math.PI, Math.PI * 2.5, false);
ctx.lineTo(W * 0.42, H * 0.63);
ctx.closePath();
ctx.fill();
// --- Header and Titles ---
ctx.fillStyle = '#003865';
ctx.font = 'bold 48px Arial';
ctx.textAlign = 'center';
ctx.fillText('MINNESOTA', W / 2, 60);
ctx.font = 'bold 24px Arial';
ctx.fillText("THE NORTH STAR STATE", W / 2, 90);
ctx.fillStyle = '#d2232a';
ctx.font = 'bold 36px Arial';
ctx.fillText("DRIVER'S LICENSE", W / 2, 130);
ctx.textAlign = 'left';
// Draw simplified state seal
ctx.fillStyle = '#003865';
ctx.beginPath();
ctx.arc(60, 60, 40, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = '#fcd116';
ctx.beginPath(); // Draw a 5-point star
let rot = Math.PI / 2 * 3, x_c = 60, y_c = 60, step = Math.PI / 5;
let outerRadius = 25, innerRadius = 10;
ctx.moveTo(x_c, y_c - outerRadius);
for (let i = 0; i < 5; i++) {
ctx.lineTo(x_c + Math.cos(rot) * outerRadius, y_c + Math.sin(rot) * outerRadius);
rot += step;
ctx.lineTo(x_c + Math.cos(rot) * innerRadius, y_c + Math.sin(rot) * innerRadius);
rot += step;
}
ctx.lineTo(x_c, y_c - outerRadius);
ctx.closePath();
ctx.fill();
// --- Main Photo & Signature Area ---
const photoX = 50, photoY = 160, photoW = 250, photoH = 315;
ctx.drawImage(originalImg, photoX, photoY, photoW, photoH);
ctx.strokeStyle = '#333';
ctx.strokeRect(photoX, photoY, photoW, photoH);
// Signature
ctx.fillStyle = '#666';
ctx.font = '16px Arial';
ctx.fillText('SIGNATURE', photoX + 10, photoY + photoH + 40);
ctx.strokeStyle = '#333';
ctx.beginPath();
ctx.moveTo(photoX, photoY + photoH + 50);
ctx.lineTo(photoX + photoW, photoY + photoH + 50);
ctx.stroke();
ctx.fillStyle = '#000080'; // Dark blue ink for signature
ctx.font = '36px "Dancing Script"';
ctx.fillText(signatureText, photoX + 15, photoY + photoH + 45);
// --- Data Fields Area ---
const dataX = 330;
let currentY = 170;
// Ghost image (semi-transparent copy of the main photo)
ctx.globalAlpha = 0.25;
ctx.drawImage(originalImg, dataX + 250, 300, 180, 225);
ctx.globalAlpha = 1.0;
// License and Expiration Dates
ctx.fillStyle = '#000';
ctx.font = 'bold 24px "Courier New", monospace';
ctx.fillText(`DL ${licenseNumber}`, dataX, currentY);
ctx.fillStyle = '#d2232a';
ctx.fillText(`EXP ${expiresDate}`, dataX + 450, currentY);
currentY += 50;
// Name
ctx.fillStyle = '#000';
ctx.font = 'bold 28px Arial';
ctx.fillText(`${lastName.toUpperCase()}, ${firstName.toUpperCase()}, ${middleName.toUpperCase()}`, dataX, currentY);
currentY += 40;
// Address
ctx.font = '22px Arial';
ctx.fillText(address.toUpperCase(), dataX, currentY);
currentY += 30;
ctx.fillText(cityStateZip.toUpperCase(), dataX, currentY);
currentY += 60;
// Helper function to draw labeled data fields consistently
const drawDataField = (label, value, x, y, width) => {
ctx.fillStyle = '#d2232a';
ctx.font = 'bold 16px Arial';
ctx.fillText(label, x, y);
ctx.fillStyle = '#000';
ctx.font = 'bold 20px Arial';
ctx.fillText(value.toUpperCase(), x, y + 22);
return x + width;
};
// Draw rows of data fields
let xPos = dataX;
xPos = drawDataField('ISS', issueDate, xPos, currentY, 150);
xPos = drawDataField('DOB', dob, xPos, currentY, 150);
xPos = drawDataField('HGT', height, xPos, currentY, 120);
drawDataField('EYES', eyes, xPos, currentY, 120);
currentY += 60;
xPos = dataX;
xPos = drawDataField('SEX', sex, xPos, currentY, 150);
xPos = drawDataField('CLASS', 'D', xPos, currentY, 150); // Standard car license
xPos = drawDataField('END', 'NONE', xPos, currentY, 120); // Endorsements
drawDataField('RESTR', 'NONE', xPos, currentY, 120); // Restrictions
return canvas;
}
Apply Changes