You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(
originalImg,
headlineText = "WANTED",
subText = "DEAD OR ALIVE",
nameText = "UNKNOWN MISCREANT",
rewardText = "$10,000 REWARD",
mainFont = "Impact, Charcoal, sans-serif",
secondaryFont = "'Courier New', Courier, monospace",
textColor = "#3A2A1B",
backgroundColor = "#F5E8C7",
imageBorderColor = "#704214",
posterBorderWidth = 20,
posterBorderColor = "#5C3D2E",
imageEffect = "sepia" // "none", "grayscale", "sepia"
) {
const CANVAS_WIDTH = 600;
const CANVAS_HEIGHT = 800;
const INNER_PADDING = 20; // Padding inside the border for content elements
const canvas = document.createElement('canvas');
canvas.width = CANVAS_WIDTH;
canvas.height = CANVAS_HEIGHT;
const ctx = canvas.getContext('2d');
// Helper function for text wrapping
function _helper_drawWrappedText_internal(context, text, x, y, maxWidth, lineHeight, maxLines = 2) {
let words = text.split(' ');
let line = '';
let currentY = y;
let linesCount = 0;
let totalHeight = 0;
for(let i = 0; i < words.length; i++) {
let testLine = line + words[i] + ' ';
// futuros issues: measureText for very long strings or special chars might be slow.
let metrics = context.measureText(testLine);
let testWidth = metrics.width;
if (testWidth > maxWidth && i > 0) {
if (linesCount < maxLines) {
context.fillText(line.trim(), x, currentY);
currentY += lineHeight;
totalHeight += lineHeight;
linesCount++;
line = words[i] + ' ';
} else {
return totalHeight;
}
} else {
line = testLine;
}
}
if (linesCount < maxLines && line.trim() !== '') {
context.fillText(line.trim(), x, currentY);
totalHeight += lineHeight;
// linesCount++; // Not strictly needed here as we return totalHeight
}
return totalHeight;
}
// 1. Draw Background Color
ctx.fillStyle = backgroundColor;
ctx.fillRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
// 2. Draw Poster Border
if (posterBorderWidth > 0) {
ctx.strokeStyle = posterBorderColor;
ctx.lineWidth = posterBorderWidth;
ctx.strokeRect(
posterBorderWidth / 2,
posterBorderWidth / 2,
CANVAS_WIDTH - posterBorderWidth,
CANVAS_HEIGHT - posterBorderWidth
);
}
// Calculate effective Rcontent area boundaries
const contentAreaX_Start = posterBorderWidth;
const contentAreaY_Start = posterBorderWidth;
const contentAreaWidth = CANVAS_WIDTH - 2 * posterBorderWidth;
const contentAreaHeight = CANVAS_HEIGHT - 2 * posterBorderWidth;
let currentY = contentAreaY_Start + INNER_PADDING;
ctx.fillStyle = textColor;
ctx.textAlign = 'center';
ctx.textBaseline = 'top';
// 3. Headline Text ("WANTED")
const HEADLINE_FONT_SIZE = Math.max(20, Math.min(80, contentAreaWidth / 7));
ctx.font = `bold ${HEADLINE_FONT_SIZE}px ${mainFont}`;
ctx.fillText(headlineText.toUpperCase(), CANVAS_WIDTH / 2, currentY);
currentY += HEADLINE_FONT_SIZE + (HEADLINE_FONT_SIZE * 0.15); // Spacing
// 4. Sub Text ("DEAD OR ALIVE")
const SUBTEXT_FONT_SIZE = Math.max(14,Math.min(30, contentAreaWidth / 16));
ctx.font = `${SUBTEXT_FONT_SIZE}px ${secondaryFont}`;
ctx.fillText(subText.toUpperCase(), CANVAS_WIDTH / 2, currentY);
currentY += SUBTEXT_FONT_SIZE + (SUBTEXT_FONT_SIZE * 0.5); // Spacing
// 5. Image Processing & Drawing
let imageToDraw = originalImg;
const imgOriginalWidth = originalImg.naturalWidth || originalImg.width;
const imgOriginalHeight = originalImg.naturalHeight || originalImg.height;
if (imageEffect !== "none" && imgOriginalWidth > 0 && imgOriginalHeight > 0) {
const tempCanvas = document.createElement('canvas');
tempCanvas.width = imgOriginalWidth;
tempCanvas.height = imgOriginalHeight;
const tempCtx = tempCanvas.getContext('2d');
tempCtx.drawImage(originalImg, 0, 0, imgOriginalWidth, imgOriginalHeight);
const imageData = tempCtx.getImageData(0, 0, tempCanvas.width, tempCanvas.height);
const data = imageData.data;
if (imageEffect === "grayscale") {
for (let i = 0; i < data.length; i += 4) {
const avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
data[i] = data[i + 1] = data[i + 2] = avg;
}
} else if (imageEffect === "sepia") {
for (let i = 0; i < data.length; i += 4) {
const r = data[i], g = data[i + 1], b = data[i + 2];
data[i] = Math.min(255, (r * 0.393) + (g * 0.769) + (b * 0.189));
data[i + 1] = Math.min(255, (r * 0.349) + (g * 0.686) + (b * 0.168));
data[i + 2] = Math.min(255, (r * 0.272) + (g * 0.534) + (b * 0.131));
}
}
tempCtx.putImageData(imageData, 0, 0);
imageToDraw = tempCanvas;
}
// Calculate image display dimensions
const MAX_NAME_LINES = 2;
const NAME_FONT_SIZE = Math.max(16,Math.min(32, contentAreaWidth / 18));
const NAME_LINE_HEIGHT_FACTOR = 1.2;
const nameLineHeight = NAME_FONT_SIZE * NAME_LINE_HEIGHT_FACTOR;
const spaceForNameText = nameLineHeight * MAX_NAME_LINES + (NAME_FONT_SIZE * 0.5); // Font size + spacing after
const REWARD_FONT_SIZE = Math.max(18,Math.min(36, contentAreaWidth / 16));
const spaceForRewardText = REWARD_FONT_SIZE + INNER_PADDING; // Font size + bottom padding
const topSectionHeight = currentY - (contentAreaY_Start + INNER_PADDING);
const availableHeightForImageAndLowerText = contentAreaHeight - INNER_PADDING - topSectionHeight - INNER_PADDING; // Top padding already used, Subtract bottom padding too
const IMG_AREA_MAX_HEIGHT = Math.max(50, availableHeightForImageAndLowerText - spaceForNameText - spaceForRewardText);
const IMG_AREA_MAX_WIDTH = contentAreaWidth - 2 * INNER_PADDING;
let imgDrawWidth = imageToDraw.width;
let imgDrawHeight = imageToDraw.height;
if (imgDrawWidth <= 0 || imgDrawHeight <= 0) { // Handle case of invalid image (e.g. not loaded)
imgDrawWidth = IMG_AREA_MAX_WIDTH;
imgDrawHeight = IMG_AREA_MAX_HEIGHT / 2; // Default placeholder size
ctx.fillStyle = '#CCCCCC';
ctx.fillRect((CANVAS_WIDTH - imgDrawWidth) / 2, currentY, imgDrawWidth, imgDrawHeight);
ctx.fillStyle = textColor; // Reset fillStyle
ctx.fillText("Image Error", CANVAS_WIDTH / 2, currentY + imgDrawHeight/2 - 10);
} else {
const imgAspect = imgDrawWidth / imgDrawHeight;
if (imgDrawWidth > IMG_AREA_MAX_WIDTH) {
imgDrawWidth = IMG_AREA_MAX_WIDTH;
imgDrawHeight = imgDrawWidth / imgAspect;
}
if (imgDrawHeight > IMG_AREA_MAX_HEIGHT) {
imgDrawHeight = IMG_AREA_MAX_HEIGHT;
imgDrawWidth = imgDrawHeight * imgAspect;
}
if (imgDrawWidth > IMG_AREA_MAX_WIDTH) { // Re-check width if height adjustment pushed it over
imgDrawWidth = IMG_AREA_MAX_WIDTH;
imgDrawHeight = imgDrawWidth / imgAspect;
}
}
const imgDrawX = (CANVAS_WIDTH - imgDrawWidth) / 2;
const imgDrawY = currentY;
if (imgDrawWidth > 0 && imgDrawHeight > 0 && imageToDraw.width > 0 && imageToDraw.height > 0) { // Check again before drawing
ctx.drawImage(imageToDraw, imgDrawX, imgDrawY, imgDrawWidth, imgDrawHeight);
}
// Image Border
const IMG_BORDER_WIDTH = Math.max(2, Math.min(6, posterBorderWidth / 4));
ctx.strokeStyle = imageBorderColor;
ctx.lineWidth = IMG_BORDER_WIDTH;
ctx.strokeRect(
imgDrawX - IMG_BORDER_WIDTH / 2,
imgDrawY - IMG_BORDER_WIDTH / 2,
imgDrawWidth + IMG_BORDER_WIDTH,
imgDrawHeight + IMG_BORDER_WIDTH
);
currentY += imgDrawHeight + (NAME_FONT_SIZE * 0.5); // Spacing after image
// 6. Name Text
ctx.font = `bold ${NAME_FONT_SIZE}px ${secondaryFont}`;
const nameTextActualHeight = _helper_drawWrappedText_internal(
ctx, nameText.toUpperCase(), CANVAS_WIDTH / 2, currentY,
contentAreaWidth - 2 * INNER_PADDING, // Max width for name
nameLineHeight, MAX_NAME_LINES
);
currentY += nameTextActualHeight + (REWARD_FONT_SIZE * 0.25); // Spacing
// 7. Reward Text
ctx.font = `bold ${REWARD_FONT_SIZE}px ${secondaryFont}`;
// Adjust Y for reward text if currentY is too low, ensuring it's on canvas
const rewardY = Math.min(currentY, CANVAS_HEIGHT - posterBorderWidth - INNER_PADDING - REWARD_FONT_SIZE);
ctx.fillText(rewardText.toUpperCase(), CANVAS_WIDTH / 2, rewardY);
return canvas;
}
Apply Changes