You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(
originalImg,
licenseNum = "D123-456-78-901-0",
lastName = "PUBLIC",
firstNames = "JOHN Q",
dob = "12/31/1990",
exp = "12/31/2028",
iss = "12/31/2020",
address1 = "123 GOVERNOR SQ",
address2 = "TALLAHASSEE, FL 32301",
sex = "M",
hgt = "5-10",
classType = "E",
endorsements = "NONE",
restrictions = "NONE"
) {
// 1. Ensure the custom cursive font is loaded for the signature
const fontUrl = 'https://fonts.googleapis.com/css2?family=Dancing+Script:wght@600&display=swap';
if (!document.getElementById('font-dancing-script')) {
const link = document.createElement('link');
link.id = 'font-dancing-script';
link.rel = 'stylesheet';
link.href = fontUrl;
document.head.appendChild(link);
}
try {
await document.fonts.load('36px "Dancing Script"');
await new Promise(resolve => setTimeout(resolve, 300)); // Short buffer to ensure render readyness
} catch (e) {
console.warn("Could not explicitly await the font, continuing with fallbacks.");
}
// 2. Setup the Canvas (CR80 ID Aspect Ratio)
const canvas = document.createElement('canvas');
canvas.width = 800;
canvas.height = 504;
const ctx = canvas.getContext('2d');
// 3. Draw Realistic Procedural Background
// Base pastel gradient
const bgGrad = ctx.createLinearGradient(0, 0, 800, 504);
bgGrad.addColorStop(0, '#f2faed'); // Very pale green
bgGrad.addColorStop(0.5, '#fdebd8'); // Pale peach
bgGrad.addColorStop(1, '#dff3fc'); // Pale cyan
ctx.fillStyle = bgGrad;
ctx.fillRect(0, 0, 800, 504);
// Background State Seal / Medallion graphic (Large & Faint)
ctx.save();
ctx.translate(450, 250);
ctx.scale(3, 3);
ctx.globalAlpha = 0.05;
ctx.beginPath();
ctx.arc(0, 0, 60, 0, Math.PI * 2);
ctx.lineWidth = 15;
ctx.strokeStyle = '#daa520';
ctx.stroke();
ctx.fillStyle = '#daa520';
ctx.beginPath();
ctx.arc(0, 0, 45, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = '#fff';
ctx.font = '10px Arial';
ctx.fillText('SEAL OF FLORIDA', -40, 5);
ctx.restore();
// Large Faded text "FLORIDA" across background
ctx.save();
ctx.translate(-20, 350);
ctx.rotate(-15 * Math.PI / 180);
ctx.font = 'bold 150px Arial';
ctx.fillStyle = 'rgba(0, 50, 150, 0.04)';
ctx.fillText('FLORIDA', 0, 0);
ctx.restore();
// Microprint background lines overlay (security feature wrapper)
ctx.lineWidth = 0.5;
ctx.strokeStyle = 'rgba(0, 0, 0, 0.04)';
for (let i = 0; i < 504; i += 8) {
ctx.beginPath();
ctx.moveTo(0, i);
ctx.lineTo(800, i);
ctx.stroke();
}
// 4. Draw Header Banner
const bannerGrad = ctx.createLinearGradient(0, 0, 800, 0);
bannerGrad.addColorStop(0, '#103e87'); // Deep blue
bannerGrad.addColorStop(0.5, '#2ca5c0'); // Cyan wave
bannerGrad.addColorStop(1, '#8bc34a'); // Greenish right edge
ctx.fillStyle = bannerGrad;
ctx.fillRect(0, 0, 800, 95);
// Header Text
ctx.fillStyle = '#ffffff';
ctx.font = 'bold 50px Arial';
ctx.fillText('FLORIDA', 240, 55);
ctx.font = 'bold 18px Arial';
ctx.fillStyle = '#ffde00';
ctx.fillText('DRIVER LICENSE', 245, 80);
ctx.font = 'italic 16px Arial';
ctx.fillStyle = '#fff';
ctx.fillText('USA', 460, 55);
// Real ID Star
ctx.beginPath();
ctx.arc(740, 45, 23, 0, Math.PI * 2);
ctx.fillStyle = '#cfb53b';
ctx.fill();
ctx.fillStyle = '#fff';
ctx.font = '28px Arial';
ctx.fillText('★', 726, 56);
// 5. Helper Function to Draw Formatted Fields
function drawField(labelNum, labelText, value, x, y, valColor = '#000', valSize = 16) {
ctx.fillStyle = '#d33'; // Common red for labels
ctx.font = 'bold 11px Arial';
let offsetX = 0;
if (labelNum) {
ctx.fillText(labelNum, x, y);
offsetX = ctx.measureText(labelNum).width + 3;
}
if (labelText) {
ctx.fillStyle = '#01458e'; // Deep blue for labels text
ctx.fillText(labelText, x + offsetX, y);
}
ctx.fillStyle = valColor;
ctx.font = `bold ${valSize}px Arial`;
ctx.fillText(value, x, y + valSize + 2);
}
// 6. Map ID Data to Layout
drawField('1d', 'DL No.', licenseNum.toUpperCase(), 250, 130, '#000', 20);
drawField('1', 'Last', lastName.toUpperCase(), 250, 185, '#000', 18);
drawField('2', 'First', firstNames.toUpperCase(), 250, 240, '#000', 18);
// Address (Two lines)
drawField('8', 'Address', address1.toUpperCase(), 250, 295, '#000', 14);
ctx.fillStyle = '#000';
ctx.font = 'bold 14px Arial';
ctx.fillText(address2.toUpperCase(), 250, 330);
// Bottom Rows - Specific Data Attributes
drawField('3', 'DOB', dob, 250, 365, '#d33', 18); // DOB is visibly marked Red
drawField('15', 'Sex', sex.toUpperCase(), 370, 365, '#000', 18);
drawField('16', 'Hgt', hgt.toUpperCase(), 440, 365, '#000', 18);
drawField('4a', 'ISS', iss, 510, 365, '#000', 18);
drawField('4b', 'EXP', exp, 610, 365, '#d33', 18); // EXP is visibly marked Red
drawField('9', 'Class', classType.toUpperCase(), 250, 430, '#000', 16);
drawField('12', 'Endors', endorsements.toUpperCase(), 350, 430, '#000', 16);
drawField('12', 'Restric', restrictions.toUpperCase(), 490, 430, '#000', 16);
// Organ Donor graphic
ctx.fillStyle = '#d33';
ctx.font = '22px Arial';
ctx.fillText('♥', 745, 430);
ctx.font = 'bold 8px Arial';
ctx.fillStyle = '#000';
ctx.fillText('DONOR', 742, 442);
// 7. Image Rendering Helpers (with Object-fit Cover)
function drawImageRounded(img, x, y, w, h, radius) {
ctx.save();
ctx.beginPath();
ctx.moveTo(x + radius, y);
ctx.lineTo(x + w - radius, y);
ctx.quadraticCurveTo(x + w, y, x + w, y + radius);
ctx.lineTo(x + w, y + h - radius);
ctx.quadraticCurveTo(x + w, y + h, x + w - radius, y + h);
ctx.lineTo(x + radius, y + h);
ctx.quadraticCurveTo(x, y + h, x, y + h - radius);
ctx.lineTo(x, y + radius);
ctx.quadraticCurveTo(x, y, x + radius, y);
ctx.closePath();
ctx.clip();
const imgAspect = img.naturalWidth / img.naturalHeight;
const boxAspect = w / h;
let sX = 0, sY = 0, sW = img.naturalWidth, sH = img.naturalHeight;
if (imgAspect > boxAspect) {
sW = sH * boxAspect;
sX = (img.naturalWidth - sW) / 2;
} else {
sH = sW / boxAspect;
sY = (img.naturalHeight - sH) / 2;
}
ctx.drawImage(img, sX, sY, sW, sH, x, y, w, h);
ctx.restore();
}
// 8. Draw Ghost Photo (Secondary smaller portrait in right corner)
ctx.save();
ctx.globalAlpha = 0.35; // Faded blend
ctx.globalCompositeOperation = 'darken';
drawImageRounded(originalImg, 650, 190, 110, 140, 10);
ctx.restore();
// 9. Draw Primary Photo
drawImageRounded(originalImg, 30, 120, 190, 250, 15);
// Add delicate border around Primary Photo
ctx.lineWidth = 1;
ctx.strokeStyle = 'rgba(0, 0, 0, 0.2)';
ctx.strokeRect(30, 120, 190, 250);
// 10. Generate cursive signature below photo
ctx.font = '36px "Dancing Script", "Segoe Script", "Brush Script MT", cursive';
ctx.fillStyle = '#222';
// Use first word of firstNames and the lastName
const sigName = `${firstNames.split(' ')[0]} ${lastName}`;
ctx.fillText(sigName, 40, 430);
// 11. Add structural borders to outer template edge
ctx.lineWidth = 3;
ctx.strokeStyle = '#999';
ctx.strokeRect(1, 1, 798, 502);
return canvas;
}
Apply Changes