You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(
originalImg,
firstName = "JOHN",
lastName = "DOE",
dlNumber = "12345678",
dob = "01/01/1990",
exp = "01/01/2030",
address = "123 MAIN ST, AUSTIN, TX 78701",
sex = "M",
height = "5'-10\"",
eyes = "BRO",
issueDate = "01/01/2022"
) {
const canvas = document.createElement('canvas');
canvas.width = 856; // Standard card ratio (approx 3.375 x 2.125 inches scaled up)
canvas.height = 540;
const ctx = canvas.getContext('2d');
// Helper: Draw a 5-point star
function drawStar(cx, cy, outerRadius, innerRadius) {
ctx.beginPath();
const points = 5;
for (let i = 0; i < points * 2; i++) {
let radius = i % 2 === 0 ? outerRadius : innerRadius;
let angle = (i * Math.PI) / points - Math.PI / 2;
ctx.lineTo(cx + Math.cos(angle) * radius, cy + Math.sin(angle) * radius);
}
ctx.closePath();
}
// Helper: Draw text with label
const labelFont = 'bold 12px Arial, Helvetica, sans-serif';
const valFont = 'bold 18px Arial, Helvetica, sans-serif';
const numFont = 'bold 22px Arial, Helvetica, sans-serif';
function drawField(lbl, val, x, y, valFontOverride, valColorOverride) {
ctx.font = labelFont;
ctx.fillStyle = '#0a3266'; // Label Blue
ctx.fillText(lbl, x, y);
ctx.font = valFontOverride || valFont;
ctx.fillStyle = valColorOverride || '#000000';
let lblW = ctx.measureText(lbl).width;
ctx.fillText(val, x + lblW + 5, y);
}
// 1. Background Gradient (Modern DL style: Light pinkish/blueish blend)
const bgGrad = ctx.createLinearGradient(0, 0, canvas.width, canvas.height);
bgGrad.addColorStop(0, '#e8f4fa'); // Light blue
bgGrad.addColorStop(0.5, '#fdecfa'); // Pale pink/white
bgGrad.addColorStop(1, '#dfeaf3'); // Light blue
ctx.fillStyle = bgGrad;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 2. Security Guilloche / Wavy Patterns
ctx.save();
ctx.lineWidth = 1;
for (let i = 0; i <= 25; i++) {
ctx.beginPath();
ctx.moveTo(0, i * 25);
ctx.bezierCurveTo(
285, i * 35 - 120,
570, i * 15 + 180,
856, i * 25 - 60
);
ctx.strokeStyle = i % 2 === 0 ? 'rgba(10, 50, 255, 0.08)' : 'rgba(255, 10, 50, 0.05)';
ctx.stroke();
}
for (let i = 0; i <= 15; i++) {
ctx.beginPath();
ctx.moveTo(i * 60, 0);
ctx.bezierCurveTo(
i * 50 + 100, 180,
i * 70 - 100, 360,
i * 60, 540
);
ctx.strokeStyle = 'rgba(200, 150, 50, 0.04)';
ctx.stroke();
}
ctx.restore();
// 3. Central Background Watermark (Texas Star/Seal outline)
ctx.save();
ctx.lineWidth = 3;
ctx.strokeStyle = 'rgba(230,230,245, 0.7)';
ctx.fillStyle = 'rgba(240,240,250, 0.4)';
drawStar(470, 320, 140, 55);
ctx.fill();
ctx.stroke();
ctx.beginPath();
ctx.arc(470, 320, 200, 0, Math.PI * 2);
ctx.setLineDash([5, 10]);
ctx.strokeStyle = 'rgba(200,200,220, 0.5)';
ctx.stroke();
ctx.restore();
// 4. Header Section
ctx.fillStyle = '#0a3266'; // Navy blue
ctx.font = 'bold 54px "Times New Roman", Times, serif';
ctx.letterSpacing = "2px";
// Wait, canvas letterSpacing isn't supported everywhere, so draw normal:
ctx.fillText('TEXAS', 360, 60);
ctx.font = 'bold 22px Arial, sans-serif';
ctx.fillStyle = '#000000';
ctx.fillText('DRIVER LICENSE', 345, 90);
// Decorative Lines
ctx.beginPath();
ctx.moveTo(30, 105);
ctx.lineTo(650, 105);
ctx.lineWidth = 2;
ctx.strokeStyle = '#b22234'; // Red line
ctx.stroke();
ctx.beginPath();
ctx.moveTo(30, 110);
ctx.lineTo(826, 110);
ctx.lineWidth = 4;
ctx.strokeStyle = '#0a3266'; // Blue line
ctx.stroke();
// 5. REAL ID Gold Star (Top-right)
ctx.save();
const starX = 780;
const starY = 55;
ctx.beginPath();
ctx.arc(starX, starY, 28, 0, Math.PI * 2);
ctx.fillStyle = '#f0c72c';
ctx.fill();
ctx.fillStyle = '#ffffff';
drawStar(starX, starY, 17, 7);
ctx.fill();
ctx.restore();
// 6. Draw Portraits
const pWidth = 220;
const pHeight = 290;
const pX = 35;
const pY = 140;
const imgRatio = originalImg.width / originalImg.height;
const boxRatio = pWidth / pHeight;
let sWidth = originalImg.width;
let sHeight = originalImg.height;
let sx = 0, sy = 0;
// Crop image centering
if (imgRatio > boxRatio) {
sWidth = originalImg.height * boxRatio;
sx = (originalImg.width - sWidth) / 2;
} else {
sHeight = originalImg.width / boxRatio;
sy = (originalImg.height - sHeight) / 2;
}
// Main Portrait
ctx.save();
// Fallback rounded rect for older browsers
ctx.beginPath();
const r = 15;
ctx.moveTo(pX + r, pY);
ctx.arcTo(pX + pWidth, pY, pX + pWidth, pY + pHeight, r);
ctx.arcTo(pX + pWidth, pY + pHeight, pX, pY + pHeight, r);
ctx.arcTo(pX, pY + pHeight, pX, pY, r);
ctx.arcTo(pX, pY, pX + pWidth, pY, r);
ctx.closePath();
ctx.clip();
ctx.fillStyle = '#cccccc'; // Fallback
ctx.fillRect(pX, pY, pWidth, pHeight);
ctx.drawImage(originalImg, Math.max(0, sx), Math.max(0, sy), sWidth, sHeight, pX, pY, pWidth, pHeight);
ctx.restore();
// Frame for primary portrait
ctx.lineWidth = 2;
ctx.strokeStyle = '#999999';
ctx.stroke();
// Ghost Portrait
ctx.save();
ctx.globalAlpha = 0.35;
ctx.drawImage(originalImg, Math.max(0, sx), Math.max(0, sy), sWidth, sHeight, 660, 310, 140, 185);
ctx.restore();
// 7. Write Data Fields
const dataX = 280;
let dataY = 160;
// Row 1: DLN & Class
drawField('4d DLN', dlNumber, dataX, dataY, numFont, '#000000');
drawField('9 CLASS', 'C', 600, dataY, valFont, '#b22234');
dataY += 45;
// Row 2: DOB & EXP
drawField('3 DOB', dob, dataX, dataY, numFont, '#b22234');
drawField('4b EXP', exp, 540, dataY, numFont, '#b22234');
dataY += 45;
// Row 3: Last Name
drawField('1 LN', lastName, dataX, dataY, valFont, '#000000');
dataY += 40;
// Row 4: First Name
drawField('2 FN', firstName, dataX, dataY, valFont, '#000000');
dataY += 50;
// Row 5: Address (Handled multiline)
ctx.font = labelFont;
ctx.fillStyle = '#0a3266';
ctx.fillText('8 ADD', dataX, dataY);
ctx.font = valFont;
ctx.fillStyle = '#000000';
const addressParts = address.split(',');
if(addressParts.length > 1) {
ctx.fillText(addressParts[0].trim(), dataX + 50, dataY);
// Combine remaining parts on the second line
ctx.fillText(addressParts.slice(1).join(',').trim(), dataX + 50, dataY + 22);
} else {
ctx.fillText(address, dataX + 50, dataY);
}
dataY += 75;
// Row 6: Issue Date & Small Data
ctx.font = labelFont;
ctx.fillStyle = '#0a3266';
ctx.fillText('4a ISS', dataX, dataY - 20);
ctx.font = 'bold 15px Arial';
ctx.fillStyle = '#000000';
ctx.fillText(issueDate, dataX + 45, dataY - 20);
// Bottom Stats Line
ctx.font = labelFont; ctx.fillStyle = '#0a3266';
ctx.fillText('15 SEX', dataX, dataY + 15);
ctx.font = valFont; ctx.fillStyle = '#000000';
ctx.fillText(sex, dataX, dataY + 35);
ctx.font = labelFont; ctx.fillStyle = '#0a3266';
ctx.fillText('16 HGT', dataX + 75, dataY + 15);
ctx.font = valFont; ctx.fillStyle = '#000000';
ctx.fillText(height, dataX + 75, dataY + 35);
ctx.font = labelFont; ctx.fillStyle = '#0a3266';
ctx.fillText('17 EYES', dataX + 155, dataY + 15);
ctx.font = valFont; ctx.fillStyle = '#000000';
ctx.fillText(eyes, dataX + 155, dataY + 35);
// Endorsements / Restrictions
ctx.font = labelFont; ctx.fillStyle = '#0a3266';
ctx.fillText('12 REST', dataX + 260, dataY + 15);
ctx.font = 'bold 16px Arial'; ctx.fillStyle = '#000000';
ctx.fillText('NONE', dataX + 260, dataY + 35);
// Return the generated canvas ID
return canvas;
}
Apply Changes