You can edit the below JavaScript code to customize the image tool.
Apply Changes
/**
* Creates a realistic, high-resolution mockup of an Oklahoma State Driver’s License.
* This function generates the visual elements, design, and text fields authentic to an
* official Oklahoma I.D. card using the provided user data and photo.
*
* @param {Image} originalImg - The user's photo as a JavaScript Image object.
* @param {string} fullName - The full name to display (e.g., "MARY L SAMPLE").
* @param {string} address - The full address, with newlines represented by '\n' (e.g., "1234 SAMPLE ST\nANYTOWN OK 73101").
* @param {string} dob - The date of birth in MM-DD-YYYY format.
* @param {string} licenseNumber - The driver's license number (e.g., "S12345678").
* @param {string} issueDate - The issue date in MM-DD-YYYY format.
* @param {string} expiryDate - The expiration date in MM-DD-YYYY format.
* @param {string} gender - The gender identifier (e.g., "F").
* @param {string} height - The height in ft-in format (e.g., "5-10").
* @param {string} eyes - The eye color abbreviation (e.g., "BRO").
* @param {string} organDonor - Organ donor status. "Yes" or "Y" will display the symbol.
* @param {string} signatureText - The text to use for generating the signature.
* @returns {Promise<HTMLCanvasElement>} A canvas element containing the generated driver's license mockup.
*/
async function processImage(
originalImg,
fullName = "MARY L SAMPLE",
address = "1234 SAMPLE ST\nANYTOWN OK 73101",
dob = "01-01-1990",
licenseNumber = "S12345678",
issueDate = "01-01-2022",
expiryDate = "01-01-2030",
gender = "F",
height = "5-10",
eyes = "BRO",
organDonor = "Yes",
signatureText = "Mary Sample"
) {
// --- 1. SETUP & FONT LOADING ---
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// Standard ID size (CR80) at 300 DPI is approx 1013x638
const CARD_WIDTH = 1013;
const CARD_HEIGHT = 638;
canvas.width = CARD_WIDTH;
canvas.height = CARD_HEIGHT;
// Dynamically load a cursive Google Font for the signature
const fontName = 'Dancing Script';
try {
const font = `40px "${fontName}"`;
if (!document.fonts.check(font)) {
const link = document.createElement('link');
link.href = `https://fonts.googleapis.com/css2?family=${fontName.replace(' ', '+')}:wght@400&display=swap`;
link.rel = 'stylesheet';
document.head.appendChild(link);
await document.fonts.load(font);
}
} catch (e) {
console.warn(`Could not load Google Font "${fontName}". Using a default cursive font.`);
}
// --- 2. BACKGROUND ---
// Main gradient background
const bgGradient = ctx.createLinearGradient(0, 0, CARD_WIDTH, CARD_HEIGHT);
bgGradient.addColorStop(0, '#eaf5ff'); // Light blue
bgGradient.addColorStop(0.5, '#ffffff');
bgGradient.addColorStop(1, '#fff0e0'); // Light orange
ctx.fillStyle = bgGradient;
ctx.fillRect(0, 0, CARD_WIDTH, CARD_HEIGHT);
// Background pattern - Simplified Scissor-tailed Flycatcher (state bird) watermark
const drawBirdWatermark = (x, y, scale, rotation) => {
ctx.save();
ctx.translate(x, y);
ctx.scale(scale, scale);
ctx.rotate(rotation * Math.PI / 180);
ctx.beginPath();
// Body & Head
ctx.moveTo(0, 0);
ctx.quadraticCurveTo(20, -30, 40, -10);
ctx.quadraticCurveTo(50, 20, 30, 30);
ctx.arc(35, -15, 8, 0, Math.PI * 2);
// Tail
ctx.moveTo(0, 0);
ctx.lineTo(-60, 50);
ctx.moveTo(0, 0);
ctx.lineTo(-70, 30);
ctx.strokeStyle = "rgba(0, 120, 200, 0.08)";
ctx.lineWidth = 2;
ctx.stroke();
ctx.restore();
};
drawBirdWatermark(300, 300, 4, -20);
drawBirdWatermark(700, 500, 3, 15);
// Background pattern - Oklahoma state seal watermark (highly simplified)
ctx.globalAlpha = 0.06;
ctx.strokeStyle = '#0055a4';
ctx.lineWidth = 10;
const centerX = CARD_WIDTH / 2;
const centerY = CARD_HEIGHT / 2 + 50;
// Outer circle
ctx.beginPath();
ctx.arc(centerX, centerY, 200, 0, 2 * Math.PI);
ctx.stroke();
// Inner star
ctx.beginPath();
ctx.moveTo(centerX, centerY - 180);
for (let i = 0; i < 5; i++) {
ctx.lineTo(centerX + Math.cos((18 + i * 72) / 180 * Math.PI) * 180, centerY - Math.sin((18 + i * 72) / 180 * Math.PI) * 180);
ctx.lineTo(centerX + Math.cos((54 + i * 72) / 180 * Math.PI) * 70, centerY - Math.sin((54 + i * 72) / 180 * Math.PI) * 70);
}
ctx.closePath();
ctx.stroke();
ctx.globalAlpha = 1.0;
// --- 3. HEADER ---
ctx.fillStyle = '#003366'; // Dark blue header
ctx.fillRect(0, 0, CARD_WIDTH, 100);
ctx.fillStyle = 'white';
ctx.font = 'bold 60px Arial';
ctx.textAlign = 'left';
ctx.fillText('OKLAHOMA', 30, 70);
ctx.fillStyle = '#00aaff';
ctx.font = 'bold 24px Arial';
ctx.fillText('DRIVER LICENSE', 420, 65);
// Real ID compliant star in a gold circle
ctx.beginPath();
const starCenterX = 950, starCenterY = 50;
ctx.arc(starCenterX, starCenterY, 35, 0, 2 * Math.PI);
ctx.fillStyle = '#f0c400';
ctx.fill();
ctx.fillStyle = 'black';
ctx.font = '45px Arial';
ctx.textAlign = 'center';
ctx.fillText('★', starCenterX, starCenterY + 18);
ctx.textAlign = 'left';
// --- 4. MAIN PHOTO & GHOST PHOTO ---
ctx.drawImage(originalImg, 30, 120, 280, 350);
ctx.strokeStyle = '#cccccc';
ctx.lineWidth = 1;
ctx.strokeRect(30, 120, 280, 350);
// Ghost Photo
ctx.globalAlpha = 0.4;
ctx.drawImage(originalImg, 350, 320, 120, 150);
ctx.globalAlpha = 1.0;
// --- 5. TEXT FIELDS ---
const drawField = (label, value, x, y) => {
ctx.font = 'bold 16px Arial';
ctx.fillStyle = '#0078c8'; // Blue for labels
ctx.fillText(label, x, y);
ctx.font = 'bold 20px "Courier New", monospace';
ctx.fillStyle = 'black';
ctx.fillText(value, x + 55, y);
};
// License Number, Expiry, DOB are usually prominent and in red
ctx.fillStyle = '#d00000'; // Red color
ctx.font = 'bold 28px "Courier New", monospace';
ctx.fillText(licenseNumber, 350, 150);
ctx.font = 'bold 22px "Courier New", monospace';
ctx.fillText(`EXP ${expiryDate}`, 750, 150);
ctx.fillText(`DOB ${dob}`, 750, 180);
// Main details block
const startX = 350;
const startY = 220;
ctx.font = 'bold 28px Arial';
ctx.fillStyle = 'black';
const nameParts = fullName.toUpperCase().split(' ');
const lastName = nameParts.length > 1 ? nameParts.pop() : '';
const firstNameMiddle = nameParts.join(' ');
ctx.fillText(`${lastName},`, startX, startY);
ctx.fillText(firstNameMiddle, startX, startY + 35);
// Address
ctx.font = '22px Arial';
const addressLines = address.toUpperCase().split('\n');
addressLines.forEach((line, index) => {
ctx.fillText(line.trim(), startX, startY + 80 + (index * 28));
});
// Other Details
let detailsY = startY + 220;
drawField('ISS', issueDate, startX, detailsY);
drawField('SEX', gender, startX + 250, detailsY);
drawField('HGT', height, startX + 380, detailsY);
drawField('EYES', eyes, startX + 510, detailsY);
// --- 6. SIGNATURE ---
ctx.fillStyle = '#00000010';
ctx.fillRect(30, 480, 280, 70);
ctx.strokeStyle = 'black';
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(40, 530);
ctx.lineTo(300, 530);
ctx.stroke();
ctx.fillStyle = 'black';
ctx.font = `40px "${fontName}", cursive`;
ctx.textAlign = 'center';
ctx.fillText(signatureText, 170, 525);
ctx.textAlign = 'left';
// --- 7. SECURITY & OTHER ELEMENTS ---
// Organ Donor
if (organDonor && (organDonor.toLowerCase() === 'yes' || organDonor.toLowerCase() === 'y')) {
ctx.fillStyle = '#d00000';
ctx.font = '40px Arial';
ctx.fillText('❤', 485, 470);
ctx.font = 'bold 16px Arial';
ctx.fillText('DONOR', 475, 495);
}
// Barcode Simulation Area
const barX = 700;
const barY = 280;
const barW = 280;
const barH = 250;
ctx.fillStyle = 'black';
ctx.font = '14px Arial';
ctx.textAlign = 'center';
ctx.fillText("2D Barcode (Simulated)", barX + barW / 2, barY - 10);
ctx.fillStyle = '#f0f0f0';
ctx.fillRect(barX, barY, barW, barH);
ctx.strokeStyle = '#cccccc';
ctx.strokeRect(barX, barY, barW, barH);
for(let i = 0; i < barH; i += 4) {
for(let j = 0; j < barW; j += Math.random() * 8 + 2) {
if (Math.random() > 0.4) {
ctx.fillStyle = 'black';
ctx.fillRect(barX + j, barY + i, Math.random() * 10, 3);
}
}
}
ctx.textAlign = 'left';
// Bottom Decorative Bar
ctx.fillStyle = '#003366';
ctx.fillRect(0, CARD_HEIGHT - 20, CARD_WIDTH, 20);
ctx.fillStyle = '#00aaff';
ctx.fillRect(0, CARD_HEIGHT - 15, CARD_WIDTH, 10);
return canvas;
}
Apply Changes