You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, name = "JOHN Q. PUBLIC", ssn = "000 - 00 - 0000", signature = "John Q. Public", issueDate = "01/01/2023") {
// 1. Load Custom Official-looking Fonts dynamically
const fontURL = 'https://fonts.googleapis.com/css2?family=Dancing+Script:wght@700&family=Playfair+Display:wght@700&display=swap';
if (!document.querySelector(`link[href="${fontURL}"]`)) {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = fontURL;
document.head.appendChild(link);
}
// Attempt to await font loading to ensure accurate canvas rendering
try {
await Promise.race([
Promise.all([
document.fonts.load('700 36px "Dancing Script"'),
document.fonts.load('700 50px "Playfair Display"')
]),
new Promise(resolve => setTimeout(resolve, 2000)) // 2-second timeout fallback
]);
} catch (e) {
console.warn("Fonts couldn't be fully loaded, using fallback fonts.", e);
}
// 2. Setup Canvas Options
const canvas = document.createElement('canvas');
canvas.width = 800;
canvas.height = 500;
const ctx = canvas.getContext('2d');
// 3. Draw Base Background Vector Texture (Banknote / Document Security Paper Look)
ctx.fillStyle = '#e8f2fc';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.strokeStyle = 'rgba(164, 199, 235, 0.4)';
ctx.lineWidth = 1;
// Wavy horizontal lines (Guilloche effect)
for (let y = 0; y < canvas.height; y += 15) {
ctx.beginPath();
for (let x = 0; x <= canvas.width; x += 10) {
let wave1 = Math.sin(x / 40) * 15;
let wave2 = Math.cos((x + y) / 30) * 10;
ctx.lineTo(x, y + wave1 + wave2);
}
ctx.stroke();
}
// Wavy vertical lines
for (let x = 0; x < canvas.width; x += 15) {
ctx.beginPath();
for (let y = 0; y <= canvas.height; y += 10) {
let wave1 = Math.sin(y / 40) * 15;
let wave2 = Math.cos((x + y) / 30) * 10;
ctx.lineTo(x + wave1 + wave2, y);
}
ctx.stroke();
}
// 4. Draw Official Background Seal Layout
ctx.save();
ctx.translate(400, 250);
ctx.strokeStyle = 'rgba(178, 207, 237, 0.8)';
ctx.lineWidth = 20;
ctx.beginPath();
ctx.arc(0, 0, 140, 0, Math.PI * 2);
ctx.stroke();
ctx.lineWidth = 4;
ctx.strokeStyle = 'rgba(255, 255, 255, 0.8)';
ctx.setLineDash([8, 8]);
ctx.stroke();
ctx.setLineDash([]);
ctx.strokeStyle = 'rgba(178, 207, 237, 1)';
ctx.lineWidth = 1.5;
ctx.beginPath(); ctx.arc(0, 0, 165, 0, Math.PI * 2); ctx.stroke();
ctx.beginPath(); ctx.arc(0, 0, 115, 0, Math.PI * 2); ctx.stroke();
ctx.restore();
// 5. Embed Optional Uploaded Profile/Signature image as a Faint Ghost Watermark!
if (originalImg && originalImg.complete && originalImg.naturalWidth > 0) {
ctx.save();
ctx.globalAlpha = 0.15; // Low opacity to keep it a secure watermark look
const scale = Math.min(300 / originalImg.naturalWidth, 300 / originalImg.naturalHeight);
const w = originalImg.naturalWidth * scale;
const h = originalImg.naturalHeight * scale;
ctx.drawImage(originalImg, 400 - w / 2, 250 - h / 2, w, h);
ctx.restore();
}
// 6. Draw Standard Greek / Roman Style Side Pillars (Classic aesthetic)
const drawColumn = (x, y, w, h) => {
ctx.fillStyle = '#f9fbff';
ctx.fillRect(x, y, w, h);
ctx.strokeStyle = '#6b8aab';
ctx.lineWidth = 1;
ctx.strokeRect(x, y, w, h);
// Cap / Base detailed rectangles
const details = [
{ dx: -10, dy: -20, dw: 20, dh: 20 },
{ dx: -5, dy: -30, dw: 10, dh: 10 },
{ dx: -10, dy: h, dw: 20, dh: 20 },
{ dx: -5, dy: h + 20, dw: 10, dh: 10 }
];
details.forEach(d => {
ctx.fillRect(x + d.dx, y + d.dy, w + d.dw, d.dh);
ctx.strokeRect(x + d.dx, y + d.dy, w + d.dw, d.dh);
});
// Vertical Fluting Lines
for (let i = 1; i < Math.floor(w / 8); i++) {
let fx = x + i * (w / Math.floor(w / 8));
ctx.beginPath();
ctx.moveTo(fx, y);
ctx.lineTo(fx, y + h);
ctx.stroke();
}
};
drawColumn(60, 120, 50, 260); // Left Pillar
drawColumn(690, 120, 50, 260); // Right Pillar
// 7. Draw Internal Framework (Borders)
ctx.strokeStyle = '#2b4d75';
ctx.lineWidth = 4;
ctx.strokeRect(20, 20, 760, 460);
ctx.lineWidth = 1;
ctx.strokeRect(15, 15, 770, 470);
ctx.strokeRect(27, 27, 746, 446);
ctx.strokeRect(30, 30, 740, 440);
// 8. Place Template Text Info
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
// Header
ctx.fillStyle = '#0a214d'; // Navy Blue Tone
ctx.font = '700 48px "Playfair Display", Times, serif';
ctx.fillText('SOCIAL SECURITY', 400, 65);
ctx.font = '700 12px Arial, sans-serif';
ctx.fillStyle = '#0a214d';
if(ctx.letterSpacing !== undefined) ctx.letterSpacing = '1.5px'; // Standard in modern APIs
ctx.fillText('SOCIAL SECURITY ADMINISTRATION', 400, 100);
if(ctx.letterSpacing !== undefined) ctx.letterSpacing = '0px';
ctx.font = '12px Arial, sans-serif';
ctx.fillStyle = '#111';
ctx.fillText('THIS NUMBER HAS BEEN ESTABLISHED FOR', 400, 160);
// The Person's Name
ctx.font = '700 32px Courier New, Courier, monospace';
ctx.fillStyle = '#000000';
ctx.fillText(String(name).toUpperCase(), 400, 210);
// The SSN Number
ctx.font = '700 52px "Playfair Display", Times, serif';
ctx.fillStyle = '#0a214d';
ctx.fillText(String(ssn), 400, 300);
// Cursive Signature
ctx.font = '700 36px "Dancing Script", cursive';
ctx.fillStyle = '#021845';
ctx.fillText(String(signature), 400, 405);
// Signature Line
ctx.beginPath();
ctx.moveTo(220, 430);
ctx.lineTo(580, 430);
ctx.strokeStyle = '#111';
ctx.lineWidth = 1.5;
ctx.stroke();
ctx.font = '10px Arial, sans-serif';
ctx.fillStyle = '#111';
ctx.fillText('SIGNATURE', 400, 442);
// Minor Date Detail
ctx.font = '10px Arial, monospace';
ctx.textAlign = 'right';
ctx.fillText(`ISSUE: ${issueDate}`, 735, 455);
return canvas;
}
Apply Changes