You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(
originalImg,
topText = "",
bottomText = "Create your perfect",
bgColor = "#f8f9fa",
textColor = "#212529",
padding = 60,
fontSize = 48,
cornerRadius = 24
) {
// Validation and parse parameter formats
padding = Number(padding) || 0;
fontSize = Math.max(Number(fontSize) || 48, 10);
cornerRadius = Number(cornerRadius) || 0;
topText = String(topText);
bottomText = String(bottomText);
bgColor = String(bgColor);
textColor = String(textColor);
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// Scale logic to prevent giant unreasonable canvases
const maxImgWidth = 1080;
const maxImgHeight = 1080;
let scale = 1;
if (originalImg.width > maxImgWidth || originalImg.height > maxImgHeight) {
scale = Math.min(maxImgWidth / originalImg.width, maxImgHeight / originalImg.height);
}
const imgW = originalImg.width * scale;
const imgH = originalImg.height * scale;
// Establishing layout bounds
const minImgContentWidth = 600;
const canvasWidth = Math.max(imgW + (padding * 2), minImgContentWidth);
const contentWidth = canvasWidth - (padding * 2);
// Using modern web-safe styling
const fontFamily = 'system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif';
const lineHeight = fontSize * 1.3;
// Helper to calculate word wraps
const getLines = (text) => {
if (!text.trim()) return [];
const words = text.split(/\s+/);
let line = '';
let lines = [];
// Define font before taking text measurements
ctx.font = `bold ${fontSize}px ${fontFamily}`;
for (let n = 0; n < words.length; n++) {
const testLine = line + words[n] + ' ';
const metrics = ctx.measureText(testLine);
if (metrics.width > contentWidth && n > 0) {
lines.push(line.trim());
line = words[n] + ' ';
} else {
line = testLine;
}
}
lines.push(line.trim());
return lines;
};
const topLines = getLines(topText);
const bottomLines = getLines(bottomText);
// Calculate dynamic heights for text allocations
const topTextHeight = topLines.length > 0 ? (topLines.length * lineHeight) + (padding * 0.5) : 0;
const bottomTextHeight = bottomLines.length > 0 ? (bottomLines.length * lineHeight) + (padding * 0.5) : 0;
// Apply the total dimensions to canvas
canvas.width = canvasWidth;
canvas.height = imgH + (padding * 2) + topTextHeight + bottomTextHeight;
// Re-apply context properties after resize wipes them natively
ctx.textBaseline = 'top';
ctx.textAlign = 'center';
ctx.font = `bold ${fontSize}px ${fontFamily}`;
// 1. Draw solid background
ctx.fillStyle = bgColor;
ctx.fillRect(0, 0, canvas.width, canvas.height);
const imgX = (canvas.width - imgW) / 2;
const imgY = padding + topTextHeight;
const safeRadius = Math.max(0, Math.min(cornerRadius, imgW / 2, imgH / 2));
// Shared path logic for drawing shapes/masks
const buildShapePath = () => {
ctx.beginPath();
if (safeRadius > 0) {
ctx.moveTo(imgX + safeRadius, imgY);
ctx.lineTo(imgX + imgW - safeRadius, imgY);
ctx.quadraticCurveTo(imgX + imgW, imgY, imgX + imgW, imgY + safeRadius);
ctx.lineTo(imgX + imgW, imgY + imgH - safeRadius);
ctx.quadraticCurveTo(imgX + imgW, imgY + imgH, imgX + imgW - safeRadius, imgY + imgH);
ctx.lineTo(imgX + safeRadius, imgY + imgH);
ctx.quadraticCurveTo(imgX, imgY + imgH, imgX, imgY + imgH - safeRadius);
ctx.lineTo(imgX, imgY + safeRadius);
ctx.quadraticCurveTo(imgX, imgY, imgX + safeRadius, imgY);
} else {
ctx.rect(imgX, imgY, imgW, imgH);
}
ctx.closePath();
};
// 2. Draw soft drop shadow behind the image layer
ctx.save();
ctx.shadowColor = "rgba(0, 0, 0, 0.15)";
ctx.shadowBlur = 15;
ctx.shadowOffsetY = 8;
ctx.fillStyle = "#ffffff";
buildShapePath();
ctx.fill();
ctx.restore();
// 3. Draw main image over its mask
ctx.save();
buildShapePath();
ctx.clip();
ctx.drawImage(originalImg, imgX, imgY, imgW, imgH);
ctx.restore();
// 4. Render typography loops
ctx.fillStyle = textColor;
if (topLines.length > 0) {
let startY = padding;
for (let l of topLines) {
ctx.fillText(l, canvas.width / 2, startY);
startY += lineHeight;
}
}
if (bottomLines.length > 0) {
let startY = imgY + imgH + (padding * 0.5);
for (let l of bottomLines) {
ctx.fillText(l, canvas.width / 2, startY);
startY += lineHeight;
}
}
return canvas;
}
Apply Changes