You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(
originalImg,
lastName = "DOE",
firstName = "JANE",
address = "123 MAIN ST\nANYTOWN, VA 23220",
dob = "01-01-1990",
iss = "01-01-2024",
exp = "01-01-2032",
dd = "A12-34-5678",
sex = "F",
height = "5-09",
eyes = "BRO",
signature = "Jane Doe"
) {
// 1. Setup Canvas
const canvas = document.createElement('canvas');
const CARD_WIDTH = 856;
const CARD_HEIGHT = 540;
canvas.width = CARD_WIDTH;
canvas.height = CARD_HEIGHT;
const ctx = canvas.getContext('2d');
// 2. Load Resources (Fonts & SVGs)
const loadFont = async (name, url) => {
if (document.fonts.check(`12px ${name}`)) {
return;
}
const font = new FontFace(name, `url(${url})`);
try {
await font.load();
document.fonts.add(font);
} catch (e) {
console.error(`Font ${name} failed to load:`, e);
}
};
await Promise.all([
loadFont('Oswald', 'https://fonts.gstatic.com/s/oswald/v49/TK3_WkUHHAIjg75cFRf3bXL8LICs1_FvgUE.woff2'),
loadFont('RobotoCondensed', 'https://fonts.gstatic.com/s/robotocondensed/v25/ieVi2ZhZI2eCN5jzbjEETS9weq8-33mZKCMS1upZQ_cehA.woff2'),
loadFont('DancingScript', 'https://fonts.gstatic.com/s/dancingscript/v24/If2SXTr6YS-zF4S-kcSWSVi_szLviuEViw.woff2')
]);
const loadImageFromSVG = async (svgString) => {
const img = new Image();
const svgBlob = new Blob([svgString], {
type: 'image/svg+xml;charset=utf-8'
});
const url = URL.createObjectURL(svgBlob);
return new Promise((resolve, reject) => {
img.onload = () => {
URL.revokeObjectURL(url);
resolve(img);
};
img.onerror = () => {
URL.revokeObjectURL(url);
reject(new Error('Failed to load image from SVG string'));
};
img.src = url;
});
};
const realIdStarSVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><circle cx="50" cy="50" r="49" fill="#000"/><path fill="#f0c400" d="M50 10l12 25 27 4 -20 19 5 27 -24 -13 -24 13 5 -27 -20 -19 27 -4z"/></svg>`;
const starImage = await loadImageFromSVG(realIdStarSVG);
// 3. Draw Background
const gradient = ctx.createLinearGradient(0, 0, 0, CARD_HEIGHT);
gradient.addColorStop(0, '#e0f4ff');
gradient.addColorStop(1, '#ffffff');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, CARD_WIDTH, CARD_HEIGHT);
// Draw faint background seal
ctx.save();
ctx.globalAlpha = 0.08;
ctx.strokeStyle = '#000000';
ctx.lineWidth = 10;
const sealX = CARD_WIDTH / 2;
const sealY = CARD_HEIGHT / 2 + 30;
const sealR = 180;
ctx.beginPath();
ctx.arc(sealX, sealY, sealR, 0, Math.PI * 2);
ctx.stroke();
ctx.font = 'bold 36px Oswald';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillStyle = '#000000';
ctx.fillText('COMMONWEALTH OF VIRGINIA', sealX, sealY);
ctx.restore();
// 4. Draw Header
ctx.fillStyle = '#003366'; // Dark blue
ctx.font = 'bold 44px Oswald';
ctx.textAlign = 'center';
ctx.fillText('VIRGINIA', CARD_WIDTH / 2, 60);
ctx.fillStyle = '#000000';
ctx.font = '22px Oswald';
ctx.fillText('DRIVER\'S LICENSE', CARD_WIDTH / 2, 90);
ctx.drawImage(starImage, CARD_WIDTH - 80, 20, 60, 60);
// 5. Draw Photos
const PHOTO_X = 40;
const PHOTO_Y = 130;
const PHOTO_W = 250;
const PHOTO_H = 300;
// Main Photo with crop-to-fit (cover)
ctx.save();
ctx.beginPath();
ctx.rect(PHOTO_X, PHOTO_Y, PHOTO_W, PHOTO_H);
ctx.clip();
const hRatio = PHOTO_W / originalImg.width;
const vRatio = PHOTO_H / originalImg.height;
const ratio = Math.max(hRatio, vRatio);
const centerShiftX = (PHOTO_W - originalImg.width * ratio) / 2;
const centerShiftY = (PHOTO_H - originalImg.height * ratio) / 2;
ctx.drawImage(originalImg, 0, 0, originalImg.width, originalImg.height,
PHOTO_X + centerShiftX, PHOTO_Y + centerShiftY, originalImg.width * ratio, originalImg.height * ratio);
ctx.restore();
ctx.strokeStyle = '#333';
ctx.lineWidth = 1;
ctx.strokeRect(PHOTO_X, PHOTO_Y, PHOTO_W, PHOTO_H);
// Ghost Photo
ctx.globalAlpha = 0.3;
ctx.drawImage(originalImg, 320, 280, 125, 150);
ctx.globalAlpha = 1.0;
// 6. Draw Text Data
ctx.textAlign = 'left';
ctx.font = 'bold 20px RobotoCondensed';
const drawDataField = (label, value, x, y, options = {}) => {
const labelWidth = options.labelWidth || 70;
const color = options.color || '#000000';
ctx.fillStyle = '#a00000';
ctx.fillText(label, x, y);
ctx.fillStyle = color;
const lines = value.split('\n');
lines.forEach((line, index) => {
ctx.fillText(line.toUpperCase(), x + labelWidth, y + (index * 25));
});
};
let yPos = 150;
const xPos = 320;
const yStep = 35;
drawDataField('DD', dd, xPos, yPos, { color: '#a00000' }); yPos += yStep;
drawDataField('EXP', exp, xPos, yPos); yPos += yStep;
yPos += 10;
drawDataField('LN', lastName, xPos, yPos); yPos += yStep;
drawDataField('FN', firstName, xPos, yPos); yPos += yStep;
yPos += 10;
const addressLines = address.split('\n');
drawDataField('ADR', address, xPos, yPos);
yPos += 25 * addressLines.length + 10;
const bottomFieldsY = yPos;
drawDataField('DOB', dob, xPos, bottomFieldsY);
drawDataField('ISS', iss, xPos, bottomFieldsY + yStep);
// Right-side data column
const xPos2 = 600;
let yPos2 = bottomFieldsY;
drawDataField('HGT', height, xPos2, yPos2, { labelWidth: 60 }); yPos2 += yStep;
drawDataField('SEX', sex, xPos2, yPos2, { labelWidth: 60 }); yPos2 += yStep;
drawDataField('EYES', eyes, xPos2, yPos2, { labelWidth: 60 });
// 7. Draw Signature
ctx.strokeStyle = '#555';
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(40, CARD_HEIGHT - 60);
ctx.lineTo(290, CARD_HEIGHT - 60);
ctx.stroke();
ctx.fillStyle = '#002244';
ctx.font = '36px DancingScript';
ctx.fillText(signature, 50, CARD_HEIGHT - 70);
// 8. Draw decorative barcode
ctx.fillStyle = '#000';
for (let i = 0; i < 200; i++) {
const x_bar = 320 + i * 2.5;
const width_bar = Math.random() > 0.5 ? 1 : 2;
ctx.fillRect(x_bar, CARD_HEIGHT - 40, width_bar, 30);
}
return canvas;
}
Apply Changes