You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(
originalImg,
threshold = 128,
invertBackground = 0, // 0 for normal, 1 for inverted background
textLine1 = "UNDERGROUND",
textLine2 = "RESISTANCE",
fontFamilyToUse = "Staatliches", // Specific font to attempt loading, e.g., "Staatliches". Falls back to Impact/Charcoal.
fontSizeRatio = 0.1, // Font size relative to canvas width
textColor = "#FFFFFF",
barColor = "#000000",
barPosition = "center", // "top", "center", "bottom"
barPaddingVerticalRatio = 0.2, // Padding above/below text within bar, relative to total text height
interlineSpacingRatio = 0.1 // Space between text lines, relative to text height of first line
) {
const STAATLICHES_FONT_SLUG = "staatliches"; // Lowercase identifier for the font
const STAATLICHES_FONT_FACE_NAME = 'UR_Flyer_Staatliches'; // Unique FontFace name
const STAATLICHES_FONT_URL = 'https://fonts.gstatic.com/s/staatliches/v10/HI_OiY8KO6hCsQSoAPmtMYeb.woff2';
let finalFontStack = fontFamilyToUse; // Default to user-specified font family
if (fontFamilyToUse.toLowerCase() === STAATLICHES_FONT_SLUG) {
// If user requests "Staatliches" (or it's the default), try to load it
finalFontStack = "Impact, Charcoal, sans-serif"; // Fallback if Staatliches fails
if (document.fonts) {
let isStaatlichesReady = false;
// Check if font is already loaded by this unique name
for (const font of document.fonts.values()) {
if (font.family === STAATLICHES_FONT_FACE_NAME) {
isStaatlichesReady = true;
break;
}
}
if (isStaatlichesReady) {
finalFontStack = `${STAATLICHES_FONT_FACE_NAME}, ${finalFontStack}`;
} else {
try {
const fontFace = new FontFace(STAATLICHES_FONT_FACE_NAME, `url(${STAATLICHES_FONT_URL})`);
await fontFace.load();
document.fonts.add(fontFace);
// console.log(`${STAATLICHES_FONT_FACE_NAME} font loaded.`);
finalFontStack = `${STAATLICHES_FONT_FACE_NAME}, ${finalFontStack}`;
} catch (e) {
console.error(`Font ${STAATLICHES_FONT_FACE_NAME} from ${STAATLICHES_FONT_URL} failed to load:`, e);
// Fallback (already set to Impact...) will be used
}
}
} else {
console.warn("document.fonts API not available. Cannot dynamically load Staatliches. Using fallback fonts.");
}
}
// else, finalFontStack remains the user-provided fontFamilyToUse
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = originalImg.naturalWidth || originalImg.width || 300;
canvas.height = originalImg.naturalHeight || originalImg.height || 150;
// Draw and Process Background Image
ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
const gray = 0.299 * r + 0.587 * g + 0.114 * b;
let outputColor = gray > threshold ? 255 : 0;
if (parseInt(invertBackground) === 1) {
outputColor = 255 - outputColor;
}
data[i] = outputColor;
data[i + 1] = outputColor;
data[i + 2] = outputColor;
// data[i+3] (alpha) remains unchanged
}
ctx.putImageData(imageData, 0, 0);
// Text Rendering
if (textLine1 || textLine2) {
const baseFontSize = Math.max(10, Math.floor(canvas.width * fontSizeRatio));
ctx.font = `${baseFontSize}px ${finalFontStack}`;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
let textVisualHeight1 = 0;
if (textLine1) {
const metrics = ctx.measureText(textLine1.toUpperCase());
const ascent = metrics.actualBoundingBoxAscent !== undefined ? metrics.actualBoundingBoxAscent : baseFontSize * 0.75;
const descent = metrics.actualBoundingBoxDescent !== undefined ? metrics.actualBoundingBoxDescent : baseFontSize * 0.25;
textVisualHeight1 = ascent + descent;
if (textVisualHeight1 <= 0 && baseFontSize > 0) textVisualHeight1 = baseFontSize; // Fallback
}
let textVisualHeight2 = 0;
if (textLine2) {
const metrics = ctx.measureText(textLine2.toUpperCase());
const ascent = metrics.actualBoundingBoxAscent !== undefined ? metrics.actualBoundingBoxAscent : baseFontSize * 0.75;
const descent = metrics.actualBoundingBoxDescent !== undefined ? metrics.actualBoundingBoxDescent : baseFontSize * 0.25;
textVisualHeight2 = ascent + descent;
if (textVisualHeight2 <= 0 && baseFontSize > 0) textVisualHeight2 = baseFontSize; // Fallback
}
const hasLine1 = (textLine1 && textVisualHeight1 > 0);
const hasLine2 = (textLine2 && textVisualHeight2 > 0);
const interlineActualSpace = (hasLine1 && hasLine2) ? (textVisualHeight1 * interlineSpacingRatio) : 0;
let totalTextContentHeight = 0;
if (hasLine1) totalTextContentHeight += textVisualHeight1;
if (hasLine2) totalTextContentHeight += textVisualHeight2;
if (hasLine1 && hasLine2) totalTextContentHeight += interlineActualSpace;
if (totalTextContentHeight === 0 && (textLine1 || textLine2)) {
let lineCountFallback = 0;
if (textLine1) lineCountFallback++; if (textLine2) lineCountFallback++;
if (lineCountFallback > 0) {
totalTextContentHeight = baseFontSize * lineCountFallback;
if (lineCountFallback > 1) totalTextContentHeight += baseFontSize * interlineSpacingRatio;
}
}
const barPaddingPx = Math.max(5, totalTextContentHeight * barPaddingVerticalRatio);
const barHeight = totalTextContentHeight + 2 * barPaddingPx;
let barY;
if (barPosition === "top") {
barY = 0;
} else if (barPosition === "bottom") {
barY = canvas.height - barHeight;
} else { // Default: center
barY = (canvas.height - barHeight) / 2;
}
barY = Math.max(0, barY);
const actualBarHeight = Math.min(barHeight, canvas.height - barY);
if (actualBarHeight > 0 && totalTextContentHeight > 0) {
ctx.fillStyle = barColor;
ctx.fillRect(0, barY, canvas.width, actualBarHeight);
ctx.fillStyle = textColor;
let currentTextYAnchor = barY + barPaddingPx; // This is the top of the first line's content area
if (hasLine1) {
const y1 = currentTextYAnchor + textVisualHeight1 / 2;
ctx.fillText(textLine1.toUpperCase(), canvas.width / 2, y1);
currentTextYAnchor += textVisualHeight1;
if (hasLine2) {
currentTextYAnchor += interlineActualSpace;
}
}
if (hasLine2) {
const y2 = currentTextYAnchor + textVisualHeight2 / 2;
ctx.fillText(textLine2.toUpperCase(), canvas.width / 2, y2);
}
}
}
return canvas;
}
Apply Changes