You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, title = "ОТВАЖНЫЙ ПОСТУПОК", subtitle = "Поступок, достойный уважения", titleColor = "#FFFFFF", subtitleColor = "#AAAAAA", bgColor = "#000000", frameColor = "#FFFFFF") {
// Helper function to calculate text wrapping
function getLines(context, text, maxWidth) {
if (!text) return [];
const words = text.split(' ');
let line = '';
let lines = [];
for (let n = 0; n < words.length; n++) {
let testLine = line + words[n] + ' ';
let metrics = context.measureText(testLine);
let testWidth = metrics.width;
if (testWidth > maxWidth && n > 0) {
lines.push(line.trim());
line = words[n] + ' ';
} else {
line = testLine;
}
}
if (line.trim() !== '') {
lines.push(line.trim());
}
return lines;
}
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// To prevent giant images from consuming too much memory/breaking the layout
const MAX_WIDTH = 800;
const MAX_HEIGHT = 800;
let imgW = originalImg.width;
let imgH = originalImg.height;
// Scale down image proportionally if it exceeds bounds
if (imgW > MAX_WIDTH || imgH > MAX_HEIGHT) {
const ratio = Math.min(MAX_WIDTH / imgW, MAX_HEIGHT / imgH);
imgW = Math.round(imgW * ratio);
imgH = Math.round(imgH * ratio);
}
// Standard demotivator ("Brave Deed") visual calculations based on width
const PADDING_SIDES = Math.max(40, Math.round(imgW * 0.12));
const PADDING_TOP = PADDING_SIDES;
const canvasWidth = imgW + PADDING_SIDES * 2;
const maxTextWidth = canvasWidth - PADDING_SIDES * 1.5;
// Responsive font sizing based on layout
const titleFontSize = Math.max(24, Math.round(canvasWidth * 0.055));
const subtitleFontSize = Math.max(16, Math.round(canvasWidth * 0.035));
// Leverage canvas context default 300x150 just for calculating text height
ctx.font = `${titleFontSize}px "Times New Roman", Times, serif`;
const titleLines = getLines(ctx, title, maxTextWidth);
const titleHeight = titleLines.length > 0 ? titleLines.length * (titleFontSize * 1.2) : 0;
ctx.font = `${subtitleFontSize}px Arial, sans-serif`;
const subtitleLines = getLines(ctx, subtitle, maxTextWidth);
const subtitleHeight = subtitleLines.length > 0 ? subtitleLines.length * (subtitleFontSize * 1.3) : 0;
// Gaps and overall height calculation
const topTextGap = Math.round(PADDING_TOP * 0.5);
const textGap = (titleLines.length > 0 && subtitleLines.length > 0) ? 15 : 0;
const bottomTextGap = Math.max(30, PADDING_SIDES);
let PADDING_BOTTOM = bottomTextGap;
if (titleLines.length > 0 || subtitleLines.length > 0) {
PADDING_BOTTOM = topTextGap + titleHeight + textGap + subtitleHeight + bottomTextGap;
}
// Set real canvas dimensions which resets previously set context styles
canvas.width = canvasWidth;
canvas.height = PADDING_TOP + imgH + PADDING_BOTTOM;
// Draw Background
ctx.fillStyle = bgColor;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Draw Base Image
ctx.drawImage(originalImg, PADDING_SIDES, PADDING_TOP, imgW, imgH);
// Draw Classic Meme Thin Border Frame Around the Image
const frameGap = Math.max(3, Math.round(imgW * 0.015));
const frameThick = Math.max(1, Math.round(imgW * 0.004));
ctx.strokeStyle = frameColor;
ctx.lineWidth = frameThick;
ctx.strokeRect(
PADDING_SIDES - frameGap - frameThick / 2,
PADDING_TOP - frameGap - frameThick / 2,
imgW + frameGap * 2 + frameThick,
imgH + frameGap * 2 + frameThick
);
// Draw Multiline Texts
let currentY = PADDING_TOP + imgH + topTextGap;
ctx.textAlign = 'center';
ctx.textBaseline = 'top';
// Output Title Text
if (titleLines.length > 0) {
ctx.font = `${titleFontSize}px "Times New Roman", Times, serif`;
ctx.fillStyle = titleColor;
titleLines.forEach(line => {
ctx.fillText(line, canvas.width / 2, currentY);
currentY += titleFontSize * 1.2;
});
}
currentY += textGap;
// Output Subtitle Text
if (subtitleLines.length > 0) {
ctx.font = `${subtitleFontSize}px Arial, sans-serif`;
ctx.fillStyle = subtitleColor;
subtitleLines.forEach(line => {
ctx.fillText(line, canvas.width / 2, currentY);
currentY += subtitleFontSize * 1.3;
});
}
return canvas;
}
Apply Changes