You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(
originalImg,
firstName = "JOHN",
lastName = "DOE",
dob = "01/01/1990",
address1 = "123 MAIN ST",
address2 = "ALBANY NY 12201",
idNumber = "123 456 789",
sex = "M",
height = "5-08",
eyes = "BRO",
issueDate = "01/01/2023",
expDate = "01/01/2031",
documentClass = "D"
) {
// 1. Prepare Font
const fontName = 'Caveat';
try {
const fontUrl = `https://fonts.googleapis.com/css2?family=${fontName}:wght@700&display=swap`;
const style = document.createElement('style');
style.textContent = `@import url('${fontUrl}');`;
document.head.appendChild(style);
await document.fonts.ready;
try {
await document.fonts.load(`700 48px ${fontName}`);
} catch (e) {}
} catch (err) {
console.warn("Could not load Caveat font, will use cursive fallback.");
}
// 2. Setup Canvas
const canvas = document.createElement("canvas");
const width = 1010;
const heightPix = 636;
canvas.width = width;
canvas.height = heightPix;
const ctx = canvas.getContext("2d");
// 3. Draw Background Gradient
const bgGradient = ctx.createLinearGradient(0, 0, width, heightPix);
bgGradient.addColorStop(0, "#cbe3fc"); // Light blue top
bgGradient.addColorStop(0.3, "#f4f8fb"); // Fading mostly white
bgGradient.addColorStop(0.7, "#fcf7eb"); // Soft yellow
bgGradient.addColorStop(1, "#f1e5ba"); // Deeper yellow base
ctx.fillStyle = bgGradient;
ctx.fillRect(0, 0, width, heightPix);
// 4. Background Guilloche/Security Lines
ctx.strokeStyle = "rgba(100, 140, 200, 0.15)";
ctx.lineWidth = 1;
for (let i = -50; i < heightPix + 50; i += 25) {
ctx.beginPath();
ctx.moveTo(0, i);
for (let x = 0; x <= width; x += 60) {
ctx.quadraticCurveTo(x + 30, i + 30, x + 60, i);
}
ctx.stroke();
}
// Abstract mountain/state silhouette in middle background
ctx.fillStyle = "rgba(10, 60, 120, 0.04)";
ctx.beginPath();
ctx.moveTo(0, 250);
ctx.lineTo(250, 150);
ctx.lineTo(500, 260);
ctx.lineTo(800, 160);
ctx.lineTo(1010, 300);
ctx.lineTo(1010, heightPix);
ctx.lineTo(0, heightPix);
ctx.closePath();
ctx.fill();
// 5. Draw Header
ctx.fillStyle = "#0A2458";
ctx.font = "bold 52px Georgia, serif";
ctx.fillText("NEW YORK STATE", 40, 70);
ctx.fillStyle = "#7A8A9E";
ctx.font = "bold 22px Arial, sans-serif";
ctx.fillText("DRIVER LICENSE", 42, 105);
// Real ID star top right
function drawStar(cx, cy, spikes, outerRadius, innerRadius) {
let rot = (Math.PI / 2) * 3;
let x = cx;
let y = cy;
let step = Math.PI / spikes;
ctx.beginPath();
ctx.moveTo(cx, cy - outerRadius);
for (let i = 0; i < spikes; i++) {
x = cx + Math.cos(rot) * outerRadius;
y = cy + Math.sin(rot) * outerRadius;
ctx.lineTo(x, y);
rot += step;
x = cx + Math.cos(rot) * innerRadius;
y = cy + Math.sin(rot) * innerRadius;
ctx.lineTo(x, y);
rot += step;
}
ctx.closePath();
ctx.fill();
}
ctx.fillStyle = "black";
ctx.beginPath();
ctx.arc(930, 70, 28, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = "gold";
drawStar(930, 70, 5, 26, 11);
// 6. Draw Photos
function createRoundRectPath(context, x, y, w, h, radius) {
context.beginPath();
context.moveTo(x + radius, y);
context.arcTo(x + w, y, x + w, y + h, radius);
context.arcTo(x + w, y + h, x, y + h, radius);
context.arcTo(x, y + h, x, y, radius);
context.arcTo(x, y, x + w, y, radius);
context.closePath();
}
function drawCroppedImage(img, dx, dy, dw, dh, alpha = 1.0) {
ctx.save();
ctx.globalAlpha = alpha;
const imgRatio = img.width / img.height;
const targetRatio = dw / dh;
let sx, sy, sw, sh;
if (imgRatio > targetRatio) {
sh = img.height;
sw = sh * targetRatio;
sx = (img.width - sw) / 2;
sy = 0;
} else {
sw = img.width;
sh = sw / targetRatio;
sx = 0;
sy = (img.height - sh) / 2;
}
createRoundRectPath(ctx, dx, dy, dw, dh, 15);
ctx.clip();
ctx.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh);
// Inner stroke
ctx.lineWidth = 1;
ctx.strokeStyle = "rgba(0,0,0,0.2)";
ctx.stroke();
ctx.restore();
}
// Main portrait
drawCroppedImage(originalImg, 40, 140, 260, 340);
// Ghost image (Bottom right)
drawCroppedImage(originalImg, 810, 370, 140, 185, 0.35);
// Simulated partial seal overlap on photo
ctx.save();
ctx.globalAlpha = 0.4;
ctx.beginPath();
ctx.arc(300, 430, 45, 0, Math.PI * 2);
ctx.strokeStyle = "#e8b923";
ctx.lineWidth = 3;
ctx.stroke();
// Seal inner text
ctx.translate(300, 430);
ctx.rotate(-Math.PI / 4);
ctx.font = "bold 14px Arial";
ctx.fillStyle = "#e8b923";
ctx.textAlign = "center";
ctx.fillText("SEAL OF NY", 0, 5);
ctx.restore();
// 7. Draw ID Data Fields
function drawField(num, label, value, x, y, valColor = "#111") {
ctx.fillStyle = "#C3212D";
ctx.font = "bold 13px Arial";
ctx.textAlign = "right";
ctx.fillText(num, x + 18, y);
ctx.fillStyle = "#555";
ctx.font = "bold 10px Arial";
ctx.textAlign = "left";
ctx.fillText(label, x + 25, y);
ctx.fillStyle = valColor;
ctx.font = "bold 23px Arial";
ctx.fillText(value, x + 25, y + 24);
}
let curY = 160;
let col1 = 330;
let col2 = 640;
let col3 = 820;
drawField("4d", "ID NUMBER", idNumber, col1, curY);
drawField("9", "CLASS", documentClass, col2, curY);
curY += 50;
drawField("3", "DOB", dob, col1, curY, "#C3212D"); // DOB in Red
drawField("4a", "ISSUED", issueDate, col2, curY);
drawField("4b", "EXPIRES", expDate, col3, curY, "#C3212D"); // Exp in Red
curY += 50;
drawField("1", "LAST NAME", lastName, col1, curY);
curY += 50;
drawField("2", "FIRST NAME", firstName, col1, curY);
curY += 50;
drawField("8", "ADDRESS", address1, col1, curY);
ctx.fillStyle = "#111";
ctx.font = "bold 23px Arial";
ctx.fillText(address2, col1 + 25, curY + 48);
curY += 85;
// Mini Table for Characteristics (Sex, Ht, Eyes)
const chY = curY;
// SEX
ctx.fillStyle = "#C3212D"; ctx.font = "bold 11px Arial"; ctx.fillText("15", col1+18, chY);
ctx.fillStyle = "#555"; ctx.font = "bold 9px Arial"; ctx.fillText("SEX", col1+25, chY);
ctx.fillStyle = "#111"; ctx.font = "bold 18px Arial"; ctx.fillText(sex, col1+25, chY+20);
// HT
ctx.fillStyle = "#C3212D"; ctx.font = "bold 11px Arial"; ctx.fillText("16", col1+98, chY);
ctx.fillStyle = "#555"; ctx.font = "bold 9px Arial"; ctx.fillText("HT", col1+105, chY);
ctx.fillStyle = "#111"; ctx.font = "bold 18px Arial"; ctx.fillText(height, col1+105, chY+20);
// EYES
ctx.fillStyle = "#C3212D"; ctx.font = "bold 11px Arial"; ctx.fillText("18", col1+178, chY);
ctx.fillStyle = "#555"; ctx.font = "bold 9px Arial"; ctx.fillText("EYES", col1+185, chY);
ctx.fillStyle = "#111"; ctx.font = "bold 18px Arial"; ctx.fillText(eyes, col1+185, chY+20);
// 8. Signature
ctx.fillStyle = "rgba(0, 0, 0, 0.85)";
ctx.font = `700 48px '${fontName}', cursive, serif`;
ctx.textAlign = "left";
// Place under the main photo roughly centered
ctx.fillText(firstName + " " + lastName, 60, 550);
// 9. Watermark (MOCK UP strictly in corners to maintain transparency and legal constraint)
function drawWatermark(x, y, rotation) {
ctx.save();
ctx.translate(x, y);
ctx.rotate(rotation);
ctx.font = "bold 64px Arial, sans-serif";
ctx.fillStyle = "rgba(220, 20, 20, 0.7)";
ctx.strokeStyle = "rgba(255, 255, 255, 0.8)";
ctx.lineWidth = 4;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.strokeText("MOCK UP", 0, 0);
ctx.fillText("MOCK UP", 0, 0);
ctx.restore();
}
// Top Right Corner Mockup
drawWatermark(width - 160, 140, -Math.PI / 6);
// Bottom Left Corner Mockup
drawWatermark(180, heightPix - 60, -Math.PI / 6);
// Frame Border
ctx.lineWidth = 3;
ctx.strokeStyle = "#ccc";
createRoundRectPath(ctx, 1, 1, width - 2, heightPix - 2, 8);
ctx.stroke();
return canvas;
}
Apply Changes