You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, targetLang = 'en', sourceLang = 'en') {
/**
* Dynamically loads a script if it's not already on the page.
* @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 (window.Tesseract) {
return resolve();
}
if (document.querySelector(`script[src="${url}"]`)) {
// If script tag exists, wait for it to load
const script = document.querySelector(`script[src="${url}"]`);
const initialOnload = script.onload;
script.onload = () => {
if(initialOnload) initialOnload();
resolve();
}
const initialOnerror = script.onerror;
script.onerror = (err) => {
if(initialOnerror) initialOnerror(err);
reject(new Error(`Script load error for ${url}`));
}
return;
}
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);
});
};
/**
* Translates text using a public, non-official Google Translate API endpoint.
* @param {string} text The text to translate.
* @param {string} from The source language code (e.g., 'en', 'auto').
* @param {string} to The target language code (e.g., 'es').
* @returns {Promise<string>} The translated text.
*/
const translateText = async (text, from, to) => {
if (!text || text.trim() === '') return '';
const url = `https://translate.googleapis.com/translate_a/single?client=gtx&sl=${from}&tl=${to}&dt=t&q=${encodeURIComponent(text)}`;
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Network response was not ok (${response.status})`);
}
const data = await response.json();
if (data && data[0]) {
return data[0].map(item => item[0]).join('');
}
return 'Translation format unknown.';
} catch (error) {
console.error('Translation failed:', error);
return `Translation Error: Could not connect to the translation service.`;
}
};
/**
* Maps common 2-letter language codes to Tesseract's 3-letter codes.
* @param {string} lang The 2-letter language code.
* @returns {string} The corresponding 3-letter code for Tesseract.
*/
const getTesseractLangCode = (lang) => {
const map = {
'en': 'eng', 'ru': 'rus', 'de': 'deu', 'fr': 'fra', 'es': 'spa',
'it': 'ita', 'pt': 'por', 'ja': 'jpn', 'ko': 'kor', 'ar': 'ara',
'hi': 'hin', 'zh-cn': 'chi_sim', 'zh-tw': 'chi_tra'
};
return map[lang.toLowerCase()] || 'eng'; // Default to English
};
/**
* Wraps text to fit within a max width 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.
* @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 lines = text.split('\n');
for (const line of lines) {
const words = line.split(' ');
let currentLine = '';
for (let n = 0; n < words.length; n++) {
const testLine = currentLine + words[n] + ' ';
const metrics = context.measureText(testLine);
const testWidth = metrics.width;
if (testWidth > maxWidth && n > 0) {
context.fillText(currentLine, x, y);
currentLine = words[n] + ' ';
y += lineHeight;
} else {
currentLine = testLine;
}
}
context.fillText(currentLine, x, y);
y += lineHeight;
}
};
// Prepare output canvas
const TEXT_AREA_HEIGHT = 150;
const PADDING = 20;
const canvas = document.createElement('canvas');
canvas.width = originalImg.width < 400 ? 400 : originalImg.width;
canvas.height = originalImg.height + TEXT_AREA_HEIGHT;
const ctx = canvas.getContext('2d');
// Draw background and image
ctx.fillStyle = '#ffffff';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(originalImg, 0, 0);
// Draw a separator
ctx.strokeStyle = '#cccccc';
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(0, originalImg.height);
ctx.lineTo(canvas.width, originalImg.height);
ctx.stroke();
/**
* Helper to draw status updates in the text area of the canvas.
* @param {string} message The message to display.
*/
const drawStatus = (message) => {
ctx.fillStyle = '#ffffff';
ctx.fillRect(0, originalImg.height + 1, canvas.width, TEXT_AREA_HEIGHT - 1);
ctx.fillStyle = '#333333';
ctx.font = '16px Arial';
ctx.textAlign = 'center';
ctx.fillText(message, canvas.width / 2, originalImg.height + TEXT_AREA_HEIGHT / 2);
};
try {
drawStatus('Loading OCR engine...');
await loadScript('https://cdn.jsdelivr.net/npm/tesseract.js@5/dist/tesseract.min.js');
const worker = await Tesseract.createWorker({
logger: m => {
if (m.status === 'recognizing text') {
drawStatus(`Recognizing Text: ${Math.round(m.progress * 100)}%`);
} else if (m.status.includes('loading language')) {
drawStatus('Loading language model...');
}
}
});
const tesseractLang = getTesseractLangCode(sourceLang);
await worker.loadLanguage(tesseractLang);
await worker.initialize(tesseractLang);
drawStatus('Recognizing Text: 0%');
const { data: { text: extractedText } } = await worker.recognize(originalImg);
await worker.terminate();
if (!extractedText || extractedText.trim() === '') {
drawStatus('No text found in the image.');
return canvas;
}
drawStatus('Translating text...');
const translatedText = await translateText(extractedText, sourceLang, targetLang);
// Clear status and draw final result
ctx.fillStyle = '#ffffff';
ctx.fillRect(0, originalImg.height + 1, canvas.width, TEXT_AREA_HEIGHT - 1);
ctx.textAlign = 'left';
ctx.fillStyle = '#000000';
const lineHeight = 22;
const startY = originalImg.height + PADDING + lineHeight/2;
const maxWidth = canvas.width - (PADDING * 2);
ctx.font = `bold 16px Arial`;
ctx.fillText(`Translated Text (${targetLang.toUpperCase()}):`, PADDING, startY);
ctx.font = `16px Arial`;
wrapText(ctx, translatedText, PADDING, startY + lineHeight, maxWidth, lineHeight);
return canvas;
} catch (error) {
console.error('An error occurred during image processing:', error);
drawStatus(`Error: ${error.message}`);
return canvas;
}
}
Apply Changes