You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(
originalImg,
title = "THE SHADOW OF DOUBT",
author = "A. WRITER",
tagline = "SOME SECRETS ARE BEST LEFT BURIED...",
titleFontColor = "#FFFFFF",
authorFontColor = "#E0E0E0",
taglineFontColor = "#D0D0D0",
overlayColor = "rgba(0,0,0,0.4)",
mainFontFamily = "Impact, 'Arial Black', Gadget, sans-serif",
taglineFontFamily = "'Palatino Linotype', 'Book Antiqua', Palatino, serif",
titleFontSize = 70,
authorFontSize = 30,
taglineFontSize = 20,
textShadowBlur = 4,
textShadowOffsetX = 2,
textShadowOffsetY = 2,
textShadowColor = "rgba(0,0,0,0.7)"
) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const canvasWidth = 600;
const canvasHeight = 900;
canvas.width = canvasWidth;
canvas.height = canvasHeight;
// 1. Draw background image (cover and crop/scale)
if (originalImg && originalImg.naturalWidth > 0 && originalImg.naturalHeight > 0) {
const imgAspect = originalImg.naturalWidth / originalImg.naturalHeight;
const canvasAspect = canvasWidth / canvasHeight;
let sWidth, sHeight, sx, sy;
if (imgAspect > canvasAspect) { // Image is wider rel. to canvas: crop image sides
sHeight = originalImg.naturalHeight;
sy = 0;
sWidth = originalImg.naturalHeight * canvasAspect;
sx = (originalImg.naturalWidth - sWidth) / 2;
} else { // Image is taller or same aspect rel. to canvas: crop image top/bottom
sWidth = originalImg.naturalWidth;
sx = 0;
sHeight = originalImg.naturalWidth / canvasAspect;
sy = (originalImg.naturalHeight - sHeight) / 2;
}
// Ensure sx, sy, sWidth, sHeight are valid and within image bounds
sx = Math.max(0, Math.round(sx)); // Round to avoid subpixel issues
sy = Math.max(0, Math.round(sy));
sWidth = Math.round(sWidth);
sHeight = Math.round(sHeight);
// check if calculated source dimensions are valid before drawing
if (sWidth > 0 && sHeight > 0 && sx + sWidth <= originalImg.naturalWidth && sy + sHeight <= originalImg.naturalHeight) {
ctx.drawImage(originalImg, sx, sy, sWidth, sHeight, 0, 0, canvasWidth, canvasHeight);
} else {
// Fallback: draw image centered, possibly letterboxed (fit, not cover) if cover calc failed
// Or more simply, draw a placeholder
ctx.fillStyle = '#333333';
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
console.warn("Image source rectangle calculation error. Drawing placeholder for image.");
}
} else {
// Fallback for invalid image object (e.g. not loaded or zero dimensions)
ctx.fillStyle = '#1a1a1a'; // Dark placeholder background
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
ctx.font = "16px Arial";
ctx.fillStyle = "#777";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText("Image Not Available", canvasWidth / 2, canvasHeight / 2);
}
// 2. Apply overlay
if (overlayColor && overlayColor.trim() !== "") {
// Basic validation attempt for color string
const tempStyle = new Option().style;
tempStyle.color = overlayColor; // Assign to a temporary style property
if (tempStyle.color !== '' || overlayColor.toLowerCase() === 'transparent') { // Check if browser could parse it or it's 'transparent'
ctx.fillStyle = overlayColor;
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
} else {
console.warn("Invalid overlayColor provided, skipping overlay:", overlayColor);
}
}
// Text styling helper functions
function applyTextStyles(fillColor) {
ctx.fillStyle = fillColor;
if (textShadowBlur > 0 || textShadowOffsetX !== 0 || textShadowOffsetY !== 0) {
if (textShadowColor && textShadowColor.trim() !== "") {
ctx.shadowColor = textShadowColor;
ctx.shadowBlur = textShadowBlur;
ctx.shadowOffsetX = textShadowOffsetX;
ctx.shadowOffsetY = textShadowOffsetY;
}
}
}
function resetTextStyles() {
ctx.shadowColor = 'rgba(0,0,0,0)';
ctx.shadowBlur = 0;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
}
ctx.textAlign = "center";
ctx.textBaseline = "middle";
// 3. Draw Tagline
if (tagline && tagline.trim() !== "") {
applyTextStyles(taglineFontColor);
ctx.font = `italic ${taglineFontSize}px ${taglineFontFamily}`;
const taglineY = canvasHeight * 0.08;
ctx.fillText(tagline.toUpperCase(), canvasWidth / 2, taglineY);
resetTextStyles();
}
// 4. Draw Title
if (title && title.trim() !== "") {
applyTextStyles(titleFontColor);
ctx.font = `bold ${titleFontSize}px ${mainFontFamily}`;
const maxTitleLineWidth = canvasWidth * 0.9;
const words = title.toUpperCase().split(' ');
let currentBuildLine = '';
const titleLinesToDraw = [];
for (let i = 0; i < words.length; i++) {
const word = words[i];
// Add space unless it's the first word in currentBuildLine or last word overall
const testLine = currentBuildLine + (currentBuildLine === '' ? '' : ' ') + word;
const metrics = ctx.measureText(testLine);
if (metrics.width > maxTitleLineWidth && currentBuildLine !== '') {
titleLinesToDraw.push(currentBuildLine);
currentBuildLine = word;
} else {
currentBuildLine = testLine;
}
}
if (currentBuildLine.trim() !== '') {
titleLinesToDraw.push(currentBuildLine);
}
const titleLineHeight = titleFontSize * 1.15;
const numTitleLines = titleLinesToDraw.length;
const titleBlockCenterY = canvasHeight * 0.30;
let currentDisplayTitleLineY = titleBlockCenterY - ((numTitleLines - 1) * titleLineHeight) / 2;
for (const singleLine of titleLinesToDraw) {
ctx.fillText(singleLine, canvasWidth / 2, currentDisplayTitleLineY);
currentDisplayTitleLineY += titleLineHeight;
}
resetTextStyles();
}
// 5. Draw Author
if (author && author.trim() !== "") {
applyTextStyles(authorFontColor);
ctx.font = `bold ${authorFontSize}px ${mainFontFamily}`;
const authorY = canvasHeight * 0.92;
ctx.fillText(author.toUpperCase(), canvasWidth / 2, authorY);
resetTextStyles();
}
return canvas;
}
Apply Changes