You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg,
// --- Canvas Settings ---
canvasWidth = 600,
canvasAspectRatio = "3:4", // Format "width:height", e.g., "3:4" for portrait
// --- Magazine Title ---
magazineTitle = "YOUR MAGAZINE",
magazineTitleFont = "Bebas Neue",
magazineTitleFontUrl = "https://fonts.gstatic.com/s/bebasneue/v9/JTUSjIg69CK48gW7PXoo9Wlhyw.woff2",
magazineTitleFallbackFont = "Impact, sans-serif",
magazineTitleSizeRatio = 0.15, // Relative to canvas width
magazineTitleColor = "white",
magazineTitleStrokeColor = "rgba(0,0,0,0.6)",
magazineTitleStrokeWidth = 3, // Pixels
magazineTitleYRatio = 0.12, // Y position of text vertical center, from top (ratio of canvas height)
magazineTitleAlign = "center", // 'left', 'center', 'right'
magazineTitleXMarginRatio = 0.05, // Margin from L/R edge if align is left/right (ratio of canvas width)
// --- Main Headline ---
headline = "AMAZING CONTENT INSIDE!",
headlineFont = "Anton",
headlineFontUrl = "https://fonts.gstatic.com/s/anton/v23/1Ptgg87LROyWN4o4CmSKZmNB.woff2",
headlineFallbackFont = "'Arial Black', Gadget, sans-serif",
headlineSizeRatio = 0.07, // Relative to canvas width
headlineColor = "yellow",
headlineBgColor = "rgba(200, 0, 0, 0.75)", // Example: Red banner background
headlineYRatio = 0.45, // Y position of the headline's vertical center (ratio of canvas height)
headlineAlign = "center", // 'left', 'center', 'right'
headlineXMarginRatio = 0.05, // Margin for L/R alignment
headlineBgFullWidth = true, // If true, BG spans canvas width. If false and centered, fits text.
headlineBgPaddingRatio = 0.25, // Padding for BG around text, relative to font size
// --- Sub-Headlines / Teasers ---
subHeadlines = "• Exclusive: The Full Story!\n• Top 10 New Discoveries\n• Stunning Photography Section", // Separate lines with \n
subFont = "Arial, sans-serif",
subFontUrl = "", // Arial is web-safe, no URL needed
subFallbackFont = "sans-serif",
subSizeRatio = 0.03, // Relative to canvas width
subColor = "white",
subBgColor = "rgba(0,0,0,0.4)", // Optional subtle background for each line; empty string for no background
subAlign = "left", // 'left', 'center', 'right'
subXRatio = 0.05, // If left: margin from left. If right: margin from right. If center: X position (0.5 for true center)
subStartYRatio = 0.25, // Y position of the top of the first sub-headline
subLineHeightRatio = 1.4, // Line height relative to sub-headline font size
subBgPaddingRatio = 0.2, // Padding for sub-headline BG
// --- Footer ---
footerTextLeft = "MARCH 2024 | VOL. 1, ISSUE 3",
footerTextRight = "$5.99 USD",
footerFont = "Arial, sans-serif",
footerFontUrl = "",
footerFallbackFont = "sans-serif",
footerSizeRatio = 0.025, // Relative to canvas width
footerColor = "white",
footerMarginRatio = 0.03, // Margin from L/R edges and bottom
// --- Barcode ---
barcodeText = "9771234567003",
barcodeBgColor = "white",
barcodeColor = "black",
barcodeHeightRatio = 0.07, // Relative to canvas height
barcodeWidthRatio = 0.12, // Relative to canvas width
barcodeXAlign = "left", // 'left' or 'right', relative to footerMarginRatio
barcodeBottomAlignYRatio = 0.08 // Distance from canvas bottom to barcode bottom edge
) {
const loadFont = async (fontName, fontUrl, fallbackFont) => {
if (!fontName || fontName.toLowerCase() === 'inherit' || fontName.toLowerCase() === 'sans-serif' || fontName.toLowerCase() === 'serif' || fontName.toLowerCase() === 'monospace') { // common generic fallbacks
return fallbackFont; // Use fallback directly if fontName is generic
}
if (!fontUrl) return `"${fontName}", ${fallbackFont}`;
try {
const font = new FontFace(fontName, `url(${fontUrl})`);
await font.load();
document.fonts.add(font);
return `"${fontName}", ${fallbackFont}`;
} catch (e) {
console.warn(`Failed to load font ${fontName} from ${fontUrl}. Using fallback. Error:`, e);
return fallbackFont;
}
};
const [
loadedMagazineTitleFontFamily,
loadedHeadlineFontFamily,
loadedSubFontFamily,
loadedFooterFontFamily
] = await Promise.all([
loadFont(magazineTitleFont, magazineTitleFontUrl, magazineTitleFallbackFont),
loadFont(headlineFont, headlineFontUrl, headlineFallbackFont),
loadFont(subFont, subFontUrl, subFallbackFont),
loadFont(footerFont, footerFontUrl, footerFallbackFont)
]);
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const ratioParts = canvasAspectRatio.split(':').map(Number);
if (ratioParts.length !== 2 || isNaN(ratioParts[0]) || isNaN(ratioParts[1]) || ratioParts[0] <= 0 || ratioParts[1] <= 0) {
console.warn("Invalid canvasAspectRatio format. Using 3:4 default.");
canvas.height = canvasWidth * (4 / 3);
} else {
canvas.height = canvasWidth * (ratioParts[1] / ratioParts[0]);
}
canvas.width = canvasWidth;
// Draw background image (cover and center)
const imgAspectRatio = originalImg.width / originalImg.height;
const canvasAspect = canvas.width / canvas.height;
let drawWidth, drawHeight, offsetX, offsetY;
if (imgAspectRatio > canvasAspect) {
drawHeight = canvas.height;
drawWidth = drawHeight * imgAspectRatio;
offsetX = (canvas.width - drawWidth) / 2;
offsetY = 0;
} else {
drawWidth = canvas.width;
drawHeight = drawWidth / imgAspectRatio;
offsetX = 0;
offsetY = (canvas.height - drawHeight) / 2;
}
ctx.drawImage(originalImg, offsetX, offsetY, drawWidth, drawHeight);
// Helper for X position based on alignment
const getTextX = (align, totalWidth, marginRatio) => {
const margin = totalWidth * marginRatio;
if (align === 'left') return margin;
if (align === 'center') return totalWidth / 2;
if (align === 'right') return totalWidth - margin;
return totalWidth / 2;
};
// --- Magazine Title ---
if (magazineTitle) {
const fontSize = canvas.width * magazineTitleSizeRatio;
ctx.font = `bold ${fontSize}px ${loadedMagazineTitleFontFamily}`;
ctx.fillStyle = magazineTitleColor;
ctx.textAlign = magazineTitleAlign;
ctx.textBaseline = 'middle';
const titleX = getTextX(magazineTitleAlign, canvas.width, magazineTitleXMarginRatio);
const titleY = canvas.height * magazineTitleYRatio;
if (magazineTitleStrokeWidth > 0 && magazineTitleStrokeColor) {
ctx.strokeStyle = magazineTitleStrokeColor;
ctx.lineWidth = magazineTitleStrokeWidth;
ctx.strokeText(magazineTitle.toUpperCase(), titleX, titleY);
}
ctx.fillText(magazineTitle.toUpperCase(), titleX, titleY);
}
// --- Main Headline ---
if (headline) {
const fontSize = canvas.width * headlineSizeRatio;
ctx.font = `bold ${fontSize}px ${loadedHeadlineFontFamily}`;
ctx.textAlign = headlineAlign;
ctx.textBaseline = 'middle';
const headlineTextX = getTextX(headlineAlign, canvas.width, headlineXMarginRatio);
const headlineTextY = canvas.height * headlineYRatio;
if (headlineBgColor) {
const textMetrics = ctx.measureText(headline.toUpperCase());
const textHeightEst = fontSize;
const padding = fontSize * headlineBgPaddingRatio;
const bgHeight = textHeightEst + padding * 2;
let bgWidth = canvas.width;
let bgX = 0;
if (!headlineBgFullWidth && headlineAlign === 'center') {
bgWidth = textMetrics.width + padding * 2;
bgX = headlineTextX - bgWidth / 2;
} else if (!headlineBgFullWidth && headlineAlign === 'left') {
bgWidth = textMetrics.width + padding * 2;
bgX = headlineTextX - padding; // headlineTextX is already margin
} else if (!headlineBgFullWidth && headlineAlign === 'right') {
bgWidth = textMetrics.width + padding * 2;
bgX = headlineTextX + padding - bgWidth; // headlineTextX is (canvas.width - margin)
}
// else, full width banner (bgX=0, bgWidth=canvas.width)
const bgY = headlineTextY - bgHeight / 2;
ctx.fillStyle = headlineBgColor;
ctx.fillRect(bgX, bgY, bgWidth, bgHeight);
}
ctx.fillStyle = headlineColor;
ctx.fillText(headline.toUpperCase(), headlineTextX, headlineTextY);
}
// --- Sub-Headlines ---
if (subHeadlines) {
const lines = subHeadlines.split('\n').filter(line => line.trim() !== '');
if (lines.length > 0) {
const fontSize = canvas.width * subSizeRatio;
ctx.font = `${fontSize}px ${loadedSubFontFamily}`;
ctx.textAlign = subAlign;
ctx.textBaseline = 'top';
let currentY = canvas.height * subStartYRatio;
const lineHeight = fontSize * subLineHeightRatio;
const padding = fontSize * subBgPaddingRatio;
let actualSubX;
if (subAlign === 'left') {
actualSubX = canvas.width * subXRatio;
} else if (subAlign === 'right') {
actualSubX = canvas.width * (1 - subXRatio); // subXRatio is margin from right
} else { // center
actualSubX = canvas.width * subXRatio; // subXRatio should be 0.5 for true center
}
for (const line of lines) {
if (subBgColor) {
const textMetrics = ctx.measureText(line);
const textWidth = textMetrics.width;
const bgH = fontSize + padding * 2;
const bgY = currentY - padding;
let bgX, bgW = textWidth + padding * 2;
if (subAlign === 'left') {
bgX = actualSubX - padding;
} else if (subAlign === 'right') {
bgX = actualSubX - textWidth - padding;
} else { // center
bgX = actualSubX - textWidth/2 - padding;
}
ctx.fillStyle = subBgColor;
ctx.fillRect(bgX, bgY, bgW, bgH);
}
ctx.fillStyle = subColor;
ctx.fillText(line, actualSubX, currentY);
currentY += lineHeight;
}
}
}
// --- Footer Text ---
const footerSideMarginPx = canvas.width * footerMarginRatio;
const footerBottomMarginPx = canvas.height * footerMarginRatio;
if (footerTextLeft || footerTextRight) {
const fontSize = canvas.width * footerSizeRatio;
ctx.font = `${fontSize}px ${loadedFooterFontFamily}`;
ctx.fillStyle = footerColor;
ctx.textBaseline = 'bottom';
const footerY = canvas.height - footerBottomMarginPx;
if (footerTextLeft) {
ctx.textAlign = 'left';
ctx.fillText(footerTextLeft, footerSideMarginPx, footerY);
}
if (footerTextRight) {
ctx.textAlign = 'right';
ctx.fillText(footerTextRight, canvas.width - footerSideMarginPx, footerY);
}
}
// --- Barcode ---
if (barcodeText) {
const bcHeight = canvas.height * barcodeHeightRatio;
const bcWidth = canvas.width * barcodeWidthRatio;
const bcBottomYPos = canvas.height - (canvas.height * barcodeBottomAlignYRatio);
const bcY = bcBottomYPos - bcHeight;
let bcX;
if (barcodeXAlign === 'left') {
bcX = footerSideMarginPx; // Align with footer text margin
} else { // 'right'
bcX = canvas.width - footerSideMarginPx - bcWidth;
}
ctx.fillStyle = barcodeBgColor;
ctx.fillRect(bcX, bcY, bcWidth, bcHeight);
ctx.fillStyle = barcodeColor;
const barPadding = bcWidth * 0.05;
let currentX = bcX + barPadding;
while(currentX < bcX + bcWidth - barPadding) {
const barWidthValue = Math.max(1, Math.random() * 2.5 + 1); // Random width 1-3.5px
if (currentX + barWidthValue <= bcX + bcWidth - barPadding) {
if (Math.random() > 0.15) { // 85% chance to draw bar
const barHeightFactor = Math.random() * 0.2 + 0.75; // Varying height (75-95%)
ctx.fillRect(currentX, bcY + bcHeight * 0.08, barWidthValue, bcHeight * 0.7 * barHeightFactor);
}
}
currentX += barWidthValue + Math.max(1, Math.random() * 1.5); // Random spacing 1-2.5px
}
const textFontSize = Math.max(6, Math.min(bcHeight * 0.18, (bcWidth * 0.9) / (barcodeText.length * 0.6)));
ctx.font = `bold ${textFontSize}px ${loadedFooterFontFamily}`;
ctx.textAlign = 'center';
ctx.textBaseline = 'bottom';
ctx.fillText(barcodeText, bcX + bcWidth / 2, bcY + bcHeight - (bcHeight * 0.05));
}
return canvas;
}
Apply Changes