You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(
originalImg,
brideName = "Jane Doe",
groomName = "John Doe",
marriageDate = "January 1, 2024",
location = "City Hall, Anytown",
officiantName = "Rev. Officiant Name",
officiantTitle = "Officiant",
witness1Name = "Witness One",
witness2Name = "Witness Two"
) {
// Helper function to load a custom font
async function loadAndApplyCustomFont(fontFamily, fontUrl) {
if (document.fonts.check(`12px "${fontFamily}"`)) {
// Font is likely already available or has been loaded.
return;
}
const newFont = new FontFace(fontFamily, `url(${fontUrl}) format('woff2')`);
try {
await newFont.load();
document.fonts.add(newFont);
// console.log(`${fontFamily} font loaded and added successfully.`);
} catch (err) {
console.error(`Failed to load font ${fontFamily} from ${fontUrl}:`, err);
// Browser will use fallback font specified in ctx.font (e.g., 'cursive', 'serif')
}
}
// Helper function to get day suffix (1st, 2nd, 3rd, th)
function getDaySuffix(d) {
if (d > 3 && d < 21) return 'th';
switch (d % 10) {
case 1: return "st";
case 2: return "nd";
case 3: return "rd";
default: return "th";
}
}
// Helper function to draw a border
function drawCertificateBorder(ctx, W, H) {
const padding = 20; // Outer padding for the border
ctx.strokeStyle = '#B08D57'; // A gold-ish brown for the border lines
// Outer border line
const outerThickness = 3; // Thickness of the main border line
ctx.lineWidth = outerThickness;
ctx.strokeRect(padding, padding, W - 2 * padding, H - 2 * padding);
// Inner, thinner border line
const gapBetweenLines = 4; // Gap between outer and inner border lines
const innerPadding = padding + outerThickness + gapBetweenLines;
const innerThickness = 1.5; // Thickness of the inner decorative line
ctx.lineWidth = innerThickness;
ctx.strokeRect(innerPadding, innerPadding, W - 2 * innerPadding, H - 2 * innerPadding);
}
// Load the "Great Vibes" font for decorative script text
// Using a common CDN link for Google Fonts' "Great Vibes"
await loadAndApplyCustomFont('Great Vibes', 'https://fonts.gstatic.com/s/greatvibes/v18/RWmMoKWR9v4hfMFldRvRIieM.woff2');
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const W = 1000; // Standard canvas width for the certificate
const H = 700; // Standard canvas height
canvas.width = W;
canvas.height = H;
// Fill background
ctx.fillStyle = '#FFF8E1'; // A light cream, parchment-like color
ctx.fillRect(0, 0, W, H);
// Draw the decorative border
drawCertificateBorder(ctx, W, H);
// --- Content Drawing Starts Here ---
ctx.fillStyle = '#4A2300'; // Dark Brown, for good contrast and classic look
ctx.textAlign = 'center'; // Most text will be centered
let currentY = 30; // Initial top margin from the border padding area
// Optional Image (e.g., a seal, emblem, or small photo)
const imgMaxDesiredHeight = 60; // Max height for the image
if (originalImg && originalImg.complete && originalImg.naturalWidth > 0 && originalImg.naturalHeight > 0) {
try {
const aspectRatio = originalImg.naturalWidth / originalImg.naturalHeight;
let imgH = imgMaxDesiredHeight;
let imgW = imgH * aspectRatio;
const maxImgWidth = W * 0.5; // Image should not exceed half canvas width
if (imgW > maxImgWidth) {
imgW = maxImgWidth;
imgH = imgW / aspectRatio;
}
const imgX = (W - imgW) / 2; // Centered horizontally
ctx.drawImage(originalImg, imgX, currentY, imgW, imgH);
currentY += imgH + 15; // Add image height and some padding below it
} catch (e) {
console.error("Error drawing original image:", e);
currentY += 15; // Fallback spacing if image fails
}
} else {
currentY += 15; // Spacing if no image is provided or loaded
}
// Main Title
ctx.font = `bold 56px "Great Vibes", cursive`; // Large, decorative font
ctx.fillText("Certificate of Marriage", W / 2, currentY);
currentY += 60; // Space after title
// Introductory phrase
ctx.font = `20px "Georgia", serif`; // Clear, classic serif font
ctx.fillText("This Certifies That", W / 2, currentY);
currentY += 30;
// Groom's Name
ctx.font = `38px "Great Vibes", cursive`;
ctx.fillText(groomName, W / 2, currentY);
currentY += 35;
// "and"
ctx.font = `18px "Georgia", serif`;
ctx.fillText("and", W / 2, currentY);
currentY += 30;
// Bride's Name
ctx.font = `38px "Great Vibes", cursive`;
ctx.fillText(brideName, W / 2, currentY);
currentY += 40;
// Declaration of union
ctx.font = `20px "Georgia", serif`;
ctx.fillText("Were united in Holy Matrimony", W / 2, currentY);
currentY += 30;
// Date of marriage
const dateObj = new Date(marriageDate); // Parse the date string
const day = dateObj.getDate();
const monthNames = ["January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"];
const month = monthNames[dateObj.getMonth()];
const year = dateObj.getFullYear();
const dateString = `on the ${day}${getDaySuffix(day)} day of ${month}, ${year}`;
ctx.font = `16px "Georgia", serif`;
ctx.fillText(dateString, W / 2, currentY);
currentY += 25;
// Location of marriage
ctx.fillText(`at ${location}`, W / 2, currentY);
currentY += 30;
// Officiant's details
ctx.font = `18px "Georgia", serif`;
ctx.fillText(`By ${officiantName}`, W / 2, currentY);
currentY += 20;
ctx.font = `italic 14px "Georgia", serif`;
ctx.fillText(officiantTitle, W / 2, currentY);
// Note: currentY is now the baseline of the officiant's title.
// --- Signature Sections ---
// These are positioned from the bottom-up relative to canvas height for stable layout
const sigLineColor = '#6D4C41'; // A slightly muted brown for signature lines
ctx.strokeStyle = sigLineColor;
ctx.lineWidth = 1; // Standard line thickness
const textOffsetY = 20; // Vertical offset for names below signature lines
const labelOffsetY = textOffsetY + 15; // Vertical offset for labels (e.g., "Groom")
// Y positions for signature lines
const mainSigLineY = H - 100; // For Groom, Bride, Officiant
const witnessLineY = mainSigLineY - 80; // For Witnesses
const witnessHeadingY = witnessLineY - 30; // For "In the presence of witnesses" text
// "In the presence of witnesses:" text
ctx.font = `16px "Georgia", serif`;
ctx.fillStyle = '#4A2300'; // Ensure text color is set
ctx.fillText("In the presence of witnesses:", W / 2, witnessHeadingY);
// Witness Signatures
const witnessSigLineWidth = W / 4.5; // Approx. 222px, adjust as needed
const witnessNameFont = `14px "Georgia", serif`;
const witnessLabelFont = `italic 12px "Georgia", serif`;
const witness1X = W / 2 - witnessSigLineWidth / 2 - 30; // Left witness
ctx.beginPath();
ctx.moveTo(witness1X - witnessSigLineWidth / 2, witnessLineY);
ctx.lineTo(witness1X + witnessSigLineWidth / 2, witnessLineY);
ctx.stroke();
ctx.font = witnessNameFont;
ctx.fillText(witness1Name, witness1X, witnessLineY + textOffsetY);
ctx.font = witnessLabelFont;
ctx.fillText("Witness", witness1X, witnessLineY + labelOffsetY);
const witness2X = W / 2 + witnessSigLineWidth / 2 + 30; // Right witness
ctx.beginPath();
ctx.moveTo(witness2X - witnessSigLineWidth / 2, witnessLineY);
ctx.lineTo(witness2X + witnessSigLineWidth / 2, witnessLineY);
ctx.stroke();
ctx.font = witnessNameFont;
ctx.fillText(witness2Name, witness2X, witnessLineY + textOffsetY);
ctx.font = witnessLabelFont;
ctx.fillText("Witness", witness2X, witnessLineY + labelOffsetY);
// Main Signatures (Groom, Bride, Officiant)
const mainSigLineWidth = W / 4.5; // Approx. 222px
const mainNameFont = `14px "Georgia", serif`;
const mainLabelFont = `italic 12px "Georgia", serif`;
const spacingBetweenMainSigs = mainSigLineWidth + 60; // Center-to-center spacing
const groomSigX = W / 2 - spacingBetweenMainSigs;
ctx.beginPath();
ctx.moveTo(groomSigX - mainSigLineWidth / 2, mainSigLineY);
ctx.lineTo(groomSigX + mainSigLineWidth / 2, mainSigLineY);
ctx.stroke();
ctx.font = mainNameFont;
ctx.fillText(groomName, groomSigX, mainSigLineY + textOffsetY);
ctx.font = mainLabelFont;
ctx.fillText("Groom", groomSigX, mainSigLineY + labelOffsetY);
const brideSigX = W / 2;
ctx.beginPath();
ctx.moveTo(brideSigX - mainSigLineWidth / 2, mainSigLineY);
ctx.lineTo(brideSigX + mainSigLineWidth / 2, mainSigLineY);
ctx.stroke();
ctx.font = mainNameFont;
ctx.fillText(brideName, brideSigX, mainSigLineY + textOffsetY);
ctx.font = mainLabelFont;
ctx.fillText("Bride", brideSigX, mainSigLineY + labelOffsetY);
const officiantSigX = W / 2 + spacingBetweenMainSigs;
ctx.beginPath();
ctx.moveTo(officiantSigX - mainSigLineWidth / 2, mainSigLineY);
ctx.lineTo(officiantSigX + mainSigLineWidth / 2, mainSigLineY);
ctx.stroke();
ctx.font = mainNameFont;
ctx.fillText(officiantName, officiantSigX, mainSigLineY + textOffsetY);
ctx.font = mainLabelFont;
ctx.fillText(officiantTitle, officiantSigX, mainSigLineY + labelOffsetY);
return canvas;
}
Apply Changes