You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(
originalImg,
text = "Advanced Search\\nWriter Tool",
fontFamily = "Arial, sans-serif",
fontSize = "60",
textColor = "#FFFFFF",
strokeColor = "#000000",
strokeWidth = "3",
shadowColor = "rgba(0,0,0,0.6)",
shadowBlur = "15",
boxColor = "rgba(0,0,0,0.5)",
boxPadding = "20",
textAlign = "center",
textPosition = "middle",
xPos = "auto",
yPos = "auto",
angleDegrees = "0",
autoWrap = "1"
) {
// Standardize canvas setup
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = originalImg.width;
canvas.height = originalImg.height;
// Draw the background image
ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
// Ensure parameters are parsed correctly
const size = parseInt(fontSize, 10) || 60;
const sWidth = parseInt(strokeWidth, 10) || 0;
const sBlur = parseInt(shadowBlur, 10) || 0;
const padding = parseInt(boxPadding, 10) || 0;
const angle = parseInt(angleDegrees, 10) || 0;
const wrap = String(autoWrap) === "1";
ctx.font = `bold ${size}px ${fontFamily}`;
ctx.textAlign = (textAlign === "left" || textAlign === "right" || textAlign === "center") ? textAlign : "center";
ctx.textBaseline = "middle";
// Split text into lines, handling both escaped strings and real newlines
let lines = [];
if (wrap) {
const segments = text.split(/\\n|\n/);
const maxW = canvas.width * 0.9; // Wrap if exceeds 90%
for (const segment of segments) {
const words = segment.split(' ');
let currentLine = words[0] || "";
for (let i = 1; i < words.length; i++) {
const word = words[i];
const width = ctx.measureText(currentLine + " " + word).width;
if (width < maxW) {
currentLine += " " + word;
} else {
lines.push(currentLine);
currentLine = word;
}
}
lines.push(currentLine);
}
} else {
lines = text.split(/\\n|\n/);
}
// Determine actual coordinates
let x = 0;
if (String(xPos).toLowerCase() === "auto") {
if (ctx.textAlign === "left") x = canvas.width * 0.05;
else if (ctx.textAlign === "right") x = canvas.width * 0.95;
else x = canvas.width / 2;
} else {
x = parseInt(xPos, 10) || 0;
}
let y = 0;
if (String(yPos).toLowerCase() === "auto") {
if (textPosition === "top") y = canvas.height * 0.15;
else if (textPosition === "bottom") y = canvas.height * 0.85;
else y = canvas.height / 2;
} else {
y = parseInt(yPos, 10) || 0;
}
// Apply translation and rotation
ctx.save();
ctx.translate(x, y);
ctx.rotate((angle * Math.PI) / 180);
// Precalculate height metrics
const lineHeight = size * 1.2;
const totalHeight = (lines.length - 1) * lineHeight;
let currentY = -totalHeight / 2;
// Draw Background Text Box if requested
if (boxColor && boxColor.toLowerCase() !== "transparent") {
let maxWidth = 0;
for (let line of lines) {
const metrics = ctx.measureText(line);
if (metrics.width > maxWidth) {
maxWidth = metrics.width;
}
}
const bWidth = maxWidth + padding * 2;
const bHeight = totalHeight + size + padding * 2;
let bx = 0;
if (ctx.textAlign === "left") bx = -padding;
else if (ctx.textAlign === "right") bx = -maxWidth - padding;
else bx = -maxWidth / 2 - padding;
const by = -totalHeight / 2 - (size / 2) - padding;
ctx.fillStyle = boxColor;
ctx.shadowColor = "transparent";
// Rounded Rectangle
const radius = Math.min(10, bWidth / 2, bHeight / 2);
ctx.beginPath();
ctx.moveTo(bx + radius, by);
ctx.lineTo(bx + bWidth - radius, by);
ctx.quadraticCurveTo(bx + bWidth, by, bx + bWidth, by + radius);
ctx.lineTo(bx + bWidth, by + bHeight - radius);
ctx.quadraticCurveTo(bx + bWidth, by + bHeight, bx + bWidth - radius, by + bHeight);
ctx.lineTo(bx + radius, by + bHeight);
ctx.quadraticCurveTo(bx, by + bHeight, bx, by + bHeight - radius);
ctx.lineTo(bx, by + radius);
ctx.quadraticCurveTo(bx, by, bx + radius, by);
ctx.closePath();
ctx.fill();
}
// Render configuration for text lines
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
// Apply drop shadow specifically to the outermost layer (Stroke if exists, else Fill)
if (sBlur > 0) {
ctx.shadowColor = shadowColor;
ctx.shadowBlur = sBlur;
ctx.shadowOffsetX = 4;
ctx.shadowOffsetY = 4;
} else {
ctx.shadowColor = "transparent";
ctx.shadowBlur = 0;
}
// Render Stroke and Text
if (sWidth > 0) {
ctx.lineWidth = sWidth;
ctx.strokeStyle = strokeColor;
ctx.lineJoin = "round"; // Limits text-spikes
ctx.miterLimit = 2;
ctx.strokeText(line, 0, currentY);
// Turn off shadow for the interior fill to keep it clean
ctx.shadowColor = "transparent";
ctx.shadowBlur = 0;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
ctx.fillStyle = textColor;
ctx.fillText(line, 0, currentY);
} else {
ctx.fillStyle = textColor;
ctx.fillText(line, 0, currentY);
}
currentY += lineHeight;
}
ctx.restore();
return canvas;
}
Apply Changes