You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, sourceLang = 'en', targetLang = 'ru') {
/**
* Helper function to dynamically load an external script.
* It ensures the script is loaded only once.
* @param {string} url - The URL of the script to load.
* @returns {Promise<void>} - A promise that resolves when the script is loaded.
*/
const loadScript = (url) => {
return new Promise((resolve, reject) => {
// If the script's global object already exists, resolve immediately.
if (window.Tesseract) {
return resolve();
}
const script = document.createElement('script');
script.src = url;
script.onload = () => resolve();
script.onerror = () => reject(new Error(`Script load error for ${url}`));
document.head.appendChild(script);
});
};
/**
* Helper function to wrap and draw text on a canvas.
* @param {CanvasRenderingContext2D} context - The canvas rendering context.
* @param {string} text - The text to wrap.
* @param {number} x - The starting x-coordinate.
* @param {number} y - The starting y-coordinate for the first line.
* @param {number} maxWidth - The maximum width of a line.
* @param {number} lineHeight - The height of each line.
*/
const wrapText = (context, text, x, y, maxWidth, lineHeight) => {
const words = text.split(' ');
let line = '';
let initialY = y;
for (let n = 0; n < words.length; n++) {
// Check if drawing another line would go outside the overlay
if (y > (initialY + context.canvas.height - lineHeight * 2)) {
context.fillText(line.trim() + '...', x, y);
return;
}
const testLine = line + words[n] + ' ';
const metrics = context.measureText(testLine);
const testWidth = metrics.width;
if (testWidth > maxWidth && n > 0) {
context.fillText(line.trim(), x, y);
line = words[n] + ' ';
y += lineHeight;
} else {
line = testLine;
}
}
context.fillText(line.trim(), x, y);
};
// Initialize the output canvas
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = originalImg.naturalWidth;
canvas.height = originalImg.naturalHeight;
/**
* Helper to draw status messages directly onto the canvas.
* @param {string} text - The status message to display.
*/
const drawStatus = (text) => {
ctx.drawImage(originalImg, 0, 0); // Redraw background image
ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
ctx.fillRect(0, canvas.height / 2 - 30, canvas.width, 60);
ctx.fillStyle = 'white';
ctx.textAlign = 'center';
ctx.font = `bold ${Math.min(24, canvas.width / 15)}px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"`;
ctx.fillText(text, canvas.width / 2, canvas.height / 2 + 8);
};
try {
drawStatus('Initializing...');
// Asynchronously load the Tesseract.js OCR library from a CDN
await loadScript('https://cdn.jsdelivr.net/npm/tesseract.js@5/dist/tesseract.min.js');
// Map standard 2-letter language codes to the 3-letter codes Tesseract uses
const langMap = { 'en': 'eng', 'ru': 'rus', 'de': 'deu', 'fr': 'fra', 'es': 'spa', 'it': 'ita', 'ja': 'jpn', 'zh': 'chi_sim' };
const tesseractLang = langMap[sourceLang] || 'eng'; // Default to English if mapping not found
// Create a Tesseract worker to perform OCR, with a logger for progress updates
drawStatus('Loading OCR Model...');
const worker = await Tesseract.createWorker(tesseractLang, 1, {
logger: m => {
if (m.status === 'recognizing text') {
const progress = (m.progress * 100).toFixed(0);
drawStatus(`Recognizing Text... ${progress}%`);
}
}
});
const { data: { text } } = await worker.recognize(originalImg);
await worker.terminate(); // Terminate worker to free up resources
if (!text || text.trim() === '') {
drawStatus('No text found.');
return canvas;
}
// Translate the extracted text using a free, no-key-required translation API
drawStatus('Translating Text...');
const apiUrl = `https://api.mymemory.translated.net/get?q=${encodeURIComponent(text)}&langpair=${sourceLang}|${targetLang}`;
const response = await fetch(apiUrl);
if (!response.ok) throw new Error(`API Error: ${response.statusText}`);
const translationData = await response.json();
const translatedText = translationData.responseData.translatedText;
// Draw the final result on the canvas
ctx.drawImage(originalImg, 0, 0);
const overlayHeight = Math.min(originalImg.naturalHeight * 0.35, 200);
const overlayY = originalImg.naturalHeight - overlayHeight;
ctx.fillStyle = 'rgba(0, 0, 0, 0.75)'; // Semi-transparent black overlay
ctx.fillRect(0, overlayY, originalImg.naturalWidth, overlayHeight);
ctx.fillStyle = 'white';
ctx.textAlign = 'left';
const padding = 15;
const fontSize = Math.max(14, Math.min(24, overlayHeight / 5));
ctx.font = `${fontSize}px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"`;
const lineHeight = fontSize * 1.3;
const startX = padding;
const startY = overlayY + padding + fontSize;
const maxWidth = originalImg.naturalWidth - (padding * 2);
wrapText(ctx, translatedText, startX, startY, maxWidth, lineHeight);
} catch (error) {
console.error('Image translation failed:', error);
ctx.drawImage(originalImg, 0, 0);
drawStatus(`Error: ${error.message}`);
}
return canvas;
}
Apply Changes