Please bookmark this page to avoid losing your image tool!

Image Language And Alphabet Linker

(Free & Supports Bulk Upload)

Drag & drop your images here or

The result will appear here...
You can edit the below JavaScript code to customize the image tool.
async function processImage(originalImg, languageName = 'Русский язык', alphabet = 'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ', fontColor = '#FFFFFF', fontSize = 24, fontFamily = 'Noto Sans', position = 'bottom', backgroundColor = '#000000', backgroundOpacity = 0.6) {

    /**
     * Helper function to dynamically load a font from Google Fonts.
     * It adds a <link> element to the document's <head> and waits for the font
     * to be loaded and ready for use before proceeding.
     * @param {string} fontFamily - The name of the font family to load.
     * @returns {Promise<void>} A promise that resolves when the font is ready.
     */
    const loadGoogleFont = (fontFamily) => {
        const fontId = `google-font-${fontFamily.replace(/\s+/g, '-')}`;
        if (document.getElementById(fontId)) {
            // Stylesheet is already present, just wait for the font to be ready.
            return document.fonts.load(`1em "${fontFamily}"`);
        }

        return new Promise((resolve, reject) => {
            const link = document.createElement('link');
            link.id = fontId;
            link.rel = 'stylesheet';
            // Request a font with a broad character set support.
            link.href = `https://fonts.googleapis.com/css2?family=${fontFamily.replace(/\s+/g, '+')}:wght@400;700&display=swap`;

            link.onload = () => {
                // The stylesheet is loaded, now explicitly wait for the font to be ready for use.
                document.fonts.load(`bold 1em "${fontFamily}"`)
                    .then(() => resolve())
                    .catch(reject); // Font file failed to load
            };
            link.onerror = reject; // Stylesheet link failed to load
            document.head.appendChild(link);
        });
    };

    // List of common web-safe fonts to avoid unnecessary network requests.
    const webSafeFonts = [
        'Arial', 'Verdana', 'Helvetica', 'Tahoma', 'Trebuchet MS', 'Times New Roman',
        'Georgia', 'Garamond', 'Courier New', 'Brush Script MT', 'sans-serif', 'serif'
    ];

    // Load the font if it's from Google Fonts (i.e., not web-safe).
    if (!webSafeFonts.find(safeFont => fontFamily.toLowerCase().includes(safeFont.toLowerCase()))) {
        try {
            await loadGoogleFont(fontFamily);
        } catch (e) {
            console.error(`Could not load font "${fontFamily}" from Google Fonts. Falling back to Arial.`, e);
            fontFamily = 'Arial'; // Fallback to a safe font
        }
    }

    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');

    canvas.width = originalImg.naturalWidth;
    canvas.height = originalImg.naturalHeight;

    // Draw the original image onto the canvas.
    ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);

    // --- Text Rendering Logic ---

    // Set font style. Using bold for better visibility.
    const fullFont = `bold ${fontSize}px ${fontFamily}`;
    ctx.font = fullFont;

    // Define text area with a margin.
    const margin = canvas.width * 0.05;
    const maxWidth = canvas.width - (margin * 2);

    // Combine language name and alphabet into a single string for wrapping.
    const fullText = `${languageName}: ${alphabet}`;

    /**
     * Breaks a string into multiple lines to fit within a max width.
     * It intelligently splits by word if spaces are present, otherwise by character.
     * @param {string} text - The input text.
     * @param {number} maxWidth - The maximum width for a line in pixels.
     * @returns {string[]} An array of strings, where each string is a line.
     */
    const breakTextIntoLines = (text, maxWidth) => {
        const containsSpaces = text.includes(' ');
        const separator = containsSpaces ? ' ' : '';
        const parts = containsSpaces ? text.split(' ') : text.split('');

        const lines = [];
        let currentLine = '';

        for (const part of parts) {
            const potentialLine = currentLine ? `${currentLine}${separator}${part}` : part;
            if (ctx.measureText(potentialLine).width > maxWidth && currentLine) {
                lines.push(currentLine.trim());
                currentLine = part;
            } else {
                currentLine = potentialLine;
            }
        }
        if (currentLine) {
            lines.push(currentLine.trim());
        }
        return lines;
    };

    const textLines = breakTextIntoLines(fullText, maxWidth);
    const lineHeight = fontSize * 1.4;
    const textBlockHeight = textLines.length * lineHeight;
    const verticalPadding = 20;

    // Calculate the Y coordinate for the top of the text block based on position.
    let startY;
    switch (position) {
        case 'top':
            startY = verticalPadding;
            break;
        case 'center':
            startY = (canvas.height - textBlockHeight) / 2 + (lineHeight - fontSize) / 2;
            break;
        case 'bottom':
        default:
            startY = canvas.height - textBlockHeight - verticalPadding + (lineHeight - fontSize) / 2;
            break;
    }

    // --- Drawing ---

    // Draw a semi-transparent background for the text to improve readability.
    if (backgroundOpacity > 0) {
        // Find the actual maximum width of all lines for a snug background fit.
        let actualMaxWidth = 0;
        textLines.forEach(line => {
            const lineWidth = ctx.measureText(line).width;
            if (lineWidth > actualMaxWidth) {
                actualMaxWidth = lineWidth;
            }
        });

        const bgPadding = fontSize * 0.5;
        const bgWidth = actualMaxWidth + bgPadding * 2;
        // Calculate height from the top of the first line to the bottom of the last.
        const bgHeight = (textLines.length - 1) * lineHeight + fontSize + bgPadding;
        const bgX = (canvas.width - bgWidth) / 2;
        const bgY = startY - bgPadding / 2;

        ctx.globalAlpha = backgroundOpacity;
        ctx.fillStyle = backgroundColor;
        ctx.fillRect(bgX, bgY, bgWidth, bgHeight);
        ctx.globalAlpha = 1.0; // Reset alpha for the text
    }

    // Draw the text lines on the canvas.
    ctx.fillStyle = fontColor;
    ctx.textAlign = 'center';
    ctx.textBaseline = 'top';

    textLines.forEach((line, i) => {
        const x = canvas.width / 2;
        const y = startY + i * lineHeight;
        ctx.fillText(line, x, y);
    });

    return canvas;
}

Free Image Tool Creator

Can't find the image tool you're looking for?
Create one based on your own needs now!

Description

The Image Language and Alphabet Linker tool allows users to overlay text onto images, featuring a specified language name and its corresponding alphabet. This tool is particularly useful for educators, language learners, and content creators who want to visually represent different languages and their writing systems. Users can customize the appearance of the text, including font style, color, size, and positioning, as well as the background opacity to enhance readability. It serves as a valuable resource for creating educational materials, social media graphics, or any visual content that requires a clear representation of language elements.

Leave a Reply

Your email address will not be published. Required fields are marked *