You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, text = "IMAGE OVERVIEW\nAdd your descriptive text here", position = "bottom", align = "center", fontSize = 32, textColor = "#000000", bgColor = "#f4f4f5") {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// Convert inputs properly
const padding = 30;
const parsedFontSize = parseInt(fontSize, 10) || 32;
const textStr = String(text).replace(/\\n/g, '\n');
let pos = String(position).toLowerCase().trim();
if (!['bottom', 'top', 'overlay'].includes(pos)) {
pos = 'bottom';
}
let alignVal = String(align).toLowerCase().trim();
if (!['left', 'right', 'center'].includes(alignVal)) {
alignVal = 'center';
}
// Modern universal sans-serif font stack
const fontFamily = "system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif";
// Pre-set font so measuring text works accurately
ctx.font = `bold ${parsedFontSize}px ${fontFamily}`;
let maxWidth = originalImg.width - (padding * 2);
if (maxWidth < 50) maxWidth = 50;
const lineHeight = parsedFontSize * 1.4;
// Helper to calculate total required height for multi-line wrapped text
function measureWrappedTextHeight(cx, txt, maxW, lineH) {
let lines = 0;
const paragraphs = txt.split('\n');
for (let i = 0; i < paragraphs.length; i++) {
const words = paragraphs[i].split(' ');
let line = '';
lines++;
for (let n = 0; n < words.length; n++) {
const testLine = line + words[n] + ' ';
const metrics = cx.measureText(testLine);
if (metrics.width > maxW && n > 0) {
lines++;
line = words[n] + ' ';
} else {
line = testLine;
}
}
}
return lines * lineH;
}
const totalTextHeight = measureWrappedTextHeight(ctx, textStr, maxWidth, lineHeight);
const textPanelHeight = totalTextHeight + (padding * 2);
let imgY = 0;
let textY = padding + parsedFontSize * 0.9;
// Setup canvas dimensions and positioning
if (pos === 'bottom') {
canvas.width = originalImg.width;
canvas.height = originalImg.height + textPanelHeight;
imgY = 0;
textY = originalImg.height + padding + parsedFontSize * 0.9; // .9 for baseline estimation
} else if (pos === 'top') {
canvas.width = originalImg.width;
canvas.height = originalImg.height + textPanelHeight;
imgY = textPanelHeight;
textY = padding + parsedFontSize * 0.9;
} else { // overlay
canvas.width = originalImg.width;
canvas.height = originalImg.height;
imgY = 0;
textY = (canvas.height / 2) - (totalTextHeight / 2) + parsedFontSize * 0.9;
}
// Paint background (for non-overlay positions)
if (pos !== 'overlay') {
ctx.fillStyle = bgColor;
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
// Draw the main image
ctx.drawImage(originalImg, 0, imgY);
// If overlay, draw a semi-transparent background block centered around the text
if (pos === 'overlay') {
ctx.globalAlpha = 0.7;
ctx.fillStyle = bgColor;
const boxY = Math.max(0, (canvas.height / 2) - (totalTextHeight / 2) - padding);
const boxH = Math.min(canvas.height, totalTextHeight + (padding * 2));
ctx.fillRect(0, boxY, canvas.width, boxH);
ctx.globalAlpha = 1.0;
}
// Setup the text styling
ctx.fillStyle = textColor;
ctx.font = `bold ${parsedFontSize}px ${fontFamily}`;
let textX;
if (alignVal === 'left') {
ctx.textAlign = 'left';
textX = padding;
} else if (alignVal === 'right') {
ctx.textAlign = 'right';
textX = canvas.width - padding;
} else {
ctx.textAlign = 'center';
textX = canvas.width / 2;
}
// Helper to render multi-line wrapped text
function renderWrappedText(cx, txt, x, y, maxW, lineH) {
const paragraphs = txt.split('\n');
let currentY = y;
for (let i = 0; i < paragraphs.length; i++) {
const words = paragraphs[i].split(' ');
let line = '';
for (let n = 0; n < words.length; n++) {
const testLine = line + words[n] + ' ';
const metrics = cx.measureText(testLine);
if (metrics.width > maxW && n > 0) {
cx.fillText(line.trim(), x, currentY);
line = words[n] + ' ';
currentY += lineH;
} else {
line = testLine;
}
}
cx.fillText(line.trim(), x, currentY);
currentY += lineH;
}
}
renderWrappedText(ctx, textStr, textX, textY, maxWidth, lineHeight);
return canvas;
}
Apply Changes