You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(
originalImg,
fullName = "CHASE PARKER BRETING",
address = "123 MAIN ST\\nANYTOWN, TX 78701",
dob = "01-01-1990",
iss = "01-01-2022",
exp = "01-01-2030",
dl = "12345678",
sex = "M",
height = "6-00",
eyes = "BRN",
cl = "C",
end = "NONE"
) {
/**
* Dynamically loads a font from Google Fonts.
* @param {string} fontFamily The name of the font family.
* @param {string} weight The font weight to load.
*/
const loadGoogleFont = async(fontFamily) => {
const fontId = `font-${fontFamily.replace(/\s+/g, '-')}`;
if (document.getElementById(fontId)) {
// Font style is already added
await document.fonts.load(`1em "${fontFamily}"`);
return;
}
const link = document.createElement('link');
link.id = fontId;
link.rel = 'stylesheet';
link.href = `https://fonts.googleapis.com/css2?family=${fontFamily.replace(/ /g, '+')}:ital,wght@0,400;0,700;1,400;1,700&display=swap`;
document.head.appendChild(link);
try {
await document.fonts.load(`1em "${fontFamily}"`);
await document.fonts.load(`italic 1em "${fontFamily}"`);
} catch (e) {
console.error(`Failed to load font: ${fontFamily}`, e);
}
};
// Load necessary fonts
await loadGoogleFont('Roboto Condensed');
await loadGoogleFont('Mea Culpa');
const canvas = document.createElement('canvas');
// Standard ID card aspect ratio (3.370 x 2.125 inches) at high resolution
const W = 1011;
const H = 638;
canvas.width = W;
canvas.height = H;
const ctx = canvas.getContext('2d');
// Create a background template image from a Base64 string to avoid external links
const bgImage = new Image();
const bgPromise = new Promise(resolve => {
bgImage.onload = () => resolve();
bgImage.onerror = () => { // Fallback to a plain color if the base64 fails
ctx.fillStyle = "#f0f4f7";
ctx.fillRect(0, 0, W, H);
resolve();
};
// This is a simplified, generated background resembling a TX license.
bgImage.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA/MAAAJoCAYAAACRDoIEAAAAAXNSR0IArs4c6QAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAD86ADAAQAAAABAAACaAAAAAD5aunDAAAEiklEQVR4Ac3UAQkAQQDAIL9L30d249ED+pB8TDoDAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgACBPw9wAdlZn7YPAAAAAElFTkSuQmCC";
});
await bgPromise;
// Draw background
ctx.drawImage(bgImage, 0, 0, W, H);
// Draw header text
ctx.fillStyle = '#0a3a6b';
ctx.font = 'bold 36px "Roboto Condensed"';
ctx.textAlign = 'left';
ctx.fillText('TEXAS', 380, 70);
ctx.fillStyle = '#111';
ctx.font = '28px "Roboto Condensed"';
ctx.fillText('DRIVER LICENSE', 380, 105);
// Draw main photo
ctx.drawImage(originalImg, 45, 145, 290, 370);
// Draw smaller "ghost" photo with transparency
ctx.globalAlpha = 0.4;
ctx.drawImage(originalImg, 410, 440, 130, 165);
ctx.globalAlpha = 1.0;
// Draw Red name field
ctx.fillStyle = '#c00000';
ctx.font = 'bold 32px "Roboto Condensed"';
ctx.fillText(fullName.toUpperCase(), 380, 230);
// Draw Address
ctx.fillStyle = 'black';
ctx.font = '26px "Roboto Condensed"';
const addressLines = address.split('\\n');
addressLines.forEach((line, index) => {
ctx.fillText(line.toUpperCase(), 380, 270 + (index * 30));
});
// Helper to draw label/data pairs
const drawField = (label, data, x, y) => {
ctx.fillStyle = '#555';
ctx.font = 'bold 20px "Roboto Condensed"';
ctx.textAlign = 'left';
ctx.fillText(label, x, y);
ctx.fillStyle = 'black';
ctx.font = '700 24px "Roboto Condensed"';
ctx.fillText(data, x + 60, y);
};
// Draw data fields
let yPos = 350;
const yStep = 38;
drawField('DOB', dob, 380, yPos);
drawField('HT', height, 380, yPos + yStep);
drawField('EYES', eyes.toUpperCase(), 380, yPos + yStep * 2);
drawField('SEX', sex.toUpperCase(), 380, yPos + yStep * 3);
drawField('CL', cl.toUpperCase(), 380, yPos + yStep * 4);
// Draw fields at the top right
const topRightX = 720;
ctx.fillStyle = 'black';
ctx.font = '24px "Roboto Condensed"';
ctx.textAlign = 'right';
ctx.fillText(`DL ${dl}`, topRightX + 240, 70);
ctx.fillText(`EXP ${exp}`, topRightX + 240, 100);
// Draw fields at the bottom
ctx.textAlign = 'left';
ctx.fillText(`ISS ${iss}`, 45, H - 40);
ctx.fillText(`END ${end.toUpperCase()}`, 250, H - 40);
// Draw signature
const nameParts = fullName.trim().split(/\s+/);
// Create a more natural looking signature from the full name
const signatureText = `${nameParts[0]} ${nameParts[nameParts.length - 1]}`;
ctx.fillStyle = '#000080'; // Dark blue ink color
ctx.font = 'italic 50px "Mea Culpa"';
ctx.fillText(signatureText, 50, 570);
// Draw REAL ID Star Icon (Gold star in a black circle)
function drawStar(cx, cy, spikes, outerRadius, innerRadius) {
let rot = Math.PI / 2 * 3;
let x = cx, y = cy;
const 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.lineTo(cx, cy - outerRadius);
ctx.closePath();
}
const starX = W - 60, starY = 65;
ctx.fillStyle = 'black';
ctx.beginPath();
ctx.arc(starX, starY, 25, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = '#e8c11a';
drawStar(starX, starY, 5, 22, 10);
ctx.fill();
return canvas;
}
Apply Changes