You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(
originalImg,
idNumber = "D7658681",
expDate = "03/30/2029",
dob = "03/30/1969",
lastName = "STEWART",
firstName = "RONALD",
address1 = "10 PELICAN CT",
address2 = "SACRAMENTO, CA 95833",
sex = "M",
hair = "BRN",
eyes = "BRO",
height = "5-06",
weight = "142",
dlClass = "C",
signatureScale = 1.0
) {
// 1. Setup Canvas (Standard ID aspect ratio scaled up for photorealism)
var canvasWidth = 856;
var canvasHeight = 540;
var canvas = document.createElement('canvas');
canvas.width = canvasWidth;
canvas.height = canvasHeight;
var ctx = canvas.getContext('2d');
// 2. Load Required Fonts Dynamically
var fontName = 'Dancing Script';
if (!document.fonts.check(`12px "${fontName}"`)) {
var link = document.createElement('link');
link.href = `https://fonts.googleapis.com/css2?family=Dancing+Script:wght@700&display=swap`;
link.rel = 'stylesheet';
document.head.appendChild(link);
try {
await document.fonts.load(`30px "${fontName}"`);
} catch (e) {
// Ignore error, will fallback to system cursive if network fails
}
}
// ensure fonts are fully applied
await document.fonts.ready;
// 3. Draw Background & Guilloche Waves
// Subtle radial gradient center
var bgGrad = ctx.createRadialGradient(canvasWidth/2, canvasHeight/2, 100, canvasWidth/2, canvasHeight/2, canvasWidth/1.1);
bgGrad.addColorStop(0, '#fff4e6'); // light yellow center
bgGrad.addColorStop(1, '#e6f2ff'); // light blue edges
ctx.fillStyle = bgGrad;
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
// Guilloche security patterns (sine/cosine waves)
ctx.lineWidth = 1;
for (var r = 0; r <= 35; r++) {
ctx.beginPath();
var yBase = (canvasHeight / 35) * r;
ctx.strokeStyle = r % 3 === 0
? 'rgba(255, 230, 150, 0.4)' // Yellow
: (r % 3 === 1 ? 'rgba(150, 200, 255, 0.35)' : 'rgba(255, 255, 255, 0.6)'); // Blue / White
for (var x = 0; x <= canvasWidth; x += 3) {
var y = yBase + Math.sin(x * 0.02 + r) * 35 + Math.cos(x * 0.015 - r) * 25;
if (x === 0) ctx.moveTo(x, y);
else ctx.lineTo(x, y);
}
ctx.stroke();
}
// Circular security rings intersecting
for (var c = 0; c < 5; c++) {
ctx.beginPath();
ctx.strokeStyle = 'rgba(255, 255, 255, 0.4)';
for (var a = 0; a <= Math.PI * 2; a += 0.05) {
var rad = 250 + Math.sin(a * 15) * 15 + c * 20;
var cx = canvasWidth/2 + Math.cos(a) * rad;
var cy = canvasHeight/2 + Math.sin(a) * rad;
if (a === 0) ctx.moveTo(cx, cy);
else ctx.lineTo(cx, cy);
}
ctx.stroke();
}
// 4. Draw Header
ctx.fillStyle = '#b3241b'; // Dark Red
ctx.font = `65px "${fontName}", cursive`;
ctx.fillText('California', 40, 75);
ctx.fillStyle = '#0b2b4d';
ctx.font = 'bold 18px Arial, sans-serif';
ctx.fillText('USA', 260, 45);
ctx.fillStyle = '#2272b5';
ctx.font = 'bold 24px Arial, sans-serif';
ctx.fillText('DRIVER LICENSE', 260, 75);
// 5. Draw Gold Bear with Star Cutout (Top Right)
var bCanvas = document.createElement('canvas');
bCanvas.width = 200; bCanvas.height = 120;
var bCtx = bCanvas.getContext('2d');
bCtx.fillStyle = '#d4af37'; // Gold
// Bear elements constructed from shapes for fidelity without huge paths
bCtx.fillRect(110, 60, 25, 45); // Back leg right
bCtx.fillRect(140, 55, 20, 50); // Back leg left
bCtx.fillRect(35, 65, 18, 45); // Front leg right
bCtx.fillRect(55, 60, 15, 45); // Front leg left
bCtx.beginPath();
bCtx.ellipse(100, 60, 75, 40, 0, 0, Math.PI * 2); // Body
bCtx.fill();
bCtx.beginPath();
bCtx.ellipse(30, 50, 35, 25, -Math.PI / 8, 0, Math.PI * 2); // Head
bCtx.fill();
bCtx.beginPath();
bCtx.ellipse(10, 60, 16, 12, -Math.PI / 6, 0, Math.PI * 2); // Snout
bCtx.fill();
bCtx.beginPath(); bCtx.arc(45, 22, 9, 0, Math.PI*2); bCtx.fill(); // Ear 1
bCtx.beginPath(); bCtx.arc(22, 26, 7, 0, Math.PI*2); bCtx.fill(); // Ear 2
// Punch out star
bCtx.globalCompositeOperation = 'destination-out';
bCtx.beginPath();
var scx = 155, scy = 30, srOuter = 16, srInner = 7;
bCtx.moveTo(scx, scy - srOuter);
for (var i = 1; i <= 10; i++) {
var rStar = i % 2 === 0 ? srOuter : srInner;
var angle = (i * Math.PI) / 5 - Math.PI / 2;
bCtx.lineTo(scx + Math.cos(angle) * rStar, scy + Math.sin(angle) * rStar);
}
bCtx.fill();
bCtx.globalCompositeOperation = 'source-over';
ctx.drawImage(bCanvas, 700, 10, 130, (130/200)*120);
// Helper to draw centered-cropped image portions
function drawCropped(canvasCtx, img, drawCx, drawCy, drawCw, drawCh) {
var imgRatio = img.width / img.height;
var tgtRatio = drawCw / drawCh;
var sx, sy, sw, sh;
if (imgRatio > tgtRatio) {
sh = img.height;
sw = img.height * tgtRatio;
sy = 0;
sx = (img.width - sw) / 2;
} else {
sw = img.width;
sh = img.width / tgtRatio;
sx = 0;
sy = (img.height - sh) / 2;
}
canvasCtx.drawImage(img, sx, sy, sw, sh, drawCx, drawCy, drawCw, drawCh);
}
// 6. Draw Left Portrait
var px = 30, py = 120, pw = 210, ph = 280, pr = 8;
ctx.save();
ctx.beginPath();
// Rounded rectangle path
ctx.moveTo(px + pr, py);
ctx.lineTo(px + pw - pr, py);
ctx.quadraticCurveTo(px + pw, py, px + pw, py + pr);
ctx.lineTo(px + pw, py + ph - pr);
ctx.quadraticCurveTo(px + pw, py + ph, px + pw - pr, py + ph);
ctx.lineTo(px + pr, py + ph);
ctx.quadraticCurveTo(px, py + ph, px, py + ph - pr);
ctx.lineTo(px, py + pr);
ctx.quadraticCurveTo(px, py, px + pr, py);
ctx.closePath();
ctx.clip();
drawCropped(ctx, originalImg, px, py, pw, ph);
ctx.restore();
// 7. Draw Ghost Portrait (Right Setup)
var gx = 650, gy = 260, gw = 150, gh = 200;
var ghCanvas = document.createElement('canvas');
ghCanvas.width = gw; ghCanvas.height = gh;
var ghCtx = ghCanvas.getContext('2d');
drawCropped(ghCtx, originalImg, 0, 0, gw, gh);
// Soft zero-border edge fade on ghost photo
ghCtx.globalCompositeOperation = 'destination-in';
var radG = ghCtx.createRadialGradient(gw/2, gh/2, 20, gw/2, gh/2, gw/2 - 5);
radG.addColorStop(0, 'rgba(0,0,0,1)');
radG.addColorStop(1, 'rgba(0,0,0,0)');
ghCtx.fillStyle = radG;
ghCtx.fillRect(0, 0, gw, gh);
ctx.save();
ctx.globalAlpha = 0.25; // Heavily faded
ctx.globalCompositeOperation = 'multiply';
ctx.drawImage(ghCanvas, gx, gy);
ctx.restore();
// 8. Draw Text Field Function Area
function drawField(label, val, x, y, valColor = 'black') {
ctx.fillStyle = '#555';
ctx.font = 'bold 11px Arial, sans-serif';
ctx.fillText(label, x, y);
var lw = label.length > 0 ? ctx.measureText(label).width + 6 : 0;
ctx.fillStyle = valColor;
ctx.font = 'bold 22px Arial, sans-serif';
ctx.fillText(val, x + lw, y);
var rw = ctx.measureText(val).width;
return x + lw + rw + 20; // Return next available X starting point
}
var dx = 265, dy = 145;
var nextX = drawField('ID', idNumber, dx, dy, '#d31515');
drawField('EXP', expDate, nextX + 15, dy, '#d31515');
dy += 36;
drawField('LN', lastName, dx, dy);
dy += 36;
drawField('FN', firstName, dx, dy);
dy += 36;
drawField('10', address1, dx, dy);
dy += 36;
drawField('', address2, dx, dy);
dy += 45;
drawField('DOB', dob, dx, dy, '#d31515');
dy += 45;
nextX = drawField('SEX', sex, dx, dy);
nextX = drawField('HAIR', hair, nextX, dy);
drawField('EYES', eyes, nextX, dy);
dy += 40;
nextX = drawField('HGT', height, dx, dy);
nextX = drawField('WGT', weight, nextX, dy);
drawField('CLASS', dlClass, nextX, dy);
// 9. Draw Signature Overlay (overlaps bottom area of text and portrait)
ctx.save();
ctx.fillStyle = 'rgba(20, 20, 30, 0.85)';
ctx.font = `${45 * signatureScale}px "${fontName}", cursive`;
var fullName = (`${firstName} ${lastName}`).toLowerCase().replace(/\b\w/g, l => l.toUpperCase());
ctx.fillText(fullName, 45, 460);
ctx.restore();
// 10. Draw Linear Barcode at Bottom Edge
var bx = 265, by = 475, bmw = 560, bmh = 45;
ctx.fillStyle = '#111';
var px_cursor = bx;
while (px_cursor < bx + bmw) {
var barW = Math.random() < 0.25 ? 4 : (Math.random() < 0.45 ? 3 : (Math.random() < 0.7 ? 2 : 1));
var spc = Math.random() < 0.4 ? 3 : (Math.random() < 0.7 ? 2 : 1);
if (px_cursor + barW > bx + bmw) barW = (bx + bmw) - px_cursor; // clamp
ctx.fillRect(px_cursor, by, barW, bmh);
px_cursor += barW + spc;
}
return canvas;
}
Apply Changes