You can edit the below JavaScript code to customize the image tool.
Apply Changes
/**
* AI Powered Image Translator Creator
* Extracts text from an image using Tesseract.js and translates it to a target language API.
* Generates and returns a single Canvas displaying the layout.
*
* @param {HTMLImageElement} originalImg - The original image object.
* @param {string} sourceLangTess - Source language for Tesseract OCR (e.g., "eng", "spa", "fra"). default is "eng".
* @param {string} sourceLangISO - Source language ISO code for Translation (e.g., "en", "es", "fr"). default is "en".
* @param {string} targetLangISO - Target language ISO code for Translation (e.g., "en", "es", "fr"). default is "es".
* @returns {Promise<HTMLCanvasElement>} A promise that resolves to the generated canvas with text translations.
*/
async function processImage(originalImg, sourceLangTess = "eng", sourceLangISO = "en", targetLangISO = "es") {
// 1. Load Tesseract.js dynamically if not available
if (typeof window.Tesseract === 'undefined') {
await new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/tesseract.js@5/dist/tesseract.min.js';
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
}
// 2. Prepare temporary canvas securely drawing the image for OCR
const tempCanvas = document.createElement('canvas');
tempCanvas.width = originalImg.width;
tempCanvas.height = originalImg.height;
const tempCtx = tempCanvas.getContext('2d');
tempCtx.drawImage(originalImg, 0, 0);
let extractedText = "";
// 3. Extract text via Tesseract OCR
try {
const worker = await window.Tesseract.createWorker(sourceLangTess);
const { data } = await worker.recognize(tempCanvas);
await worker.terminate();
extractedText = data.text.trim();
} catch (e) {
extractedText = `[OCR Error: ${e.message}]`;
}
// 4. Translate text via free Translation API (MyMemory)
let translatedText = "";
if (extractedText && !extractedText.startsWith("[OCR Error")) {
// Prevent large payload URI errors; truncate safely to 1000 characters
let textToTranslate = extractedText;
if (textToTranslate.length > 1000) {
textToTranslate = textToTranslate.substring(0, 1000) + '...';
}
try {
const url = `https://api.mymemory.translated.net/get?q=${encodeURIComponent(textToTranslate)}&langpair=${sourceLangISO}|${targetLangISO}`;
const response = await fetch(url);
const result = await response.json();
if (result && result.responseData && result.responseData.translatedText) {
translatedText = result.responseData.translatedText;
} else {
translatedText = "[Translation API returned an invalid response]";
}
} catch (e) {
translatedText = `[Translation Error: ${e.message}]`;
}
} else if (!extractedText) {
extractedText = "[No text detected in the image]";
translatedText = "[No text detected in the image]";
}
// 5. Generate final presentation canvas
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// Layout config
const imgWidth = Math.max(originalImg.width, 800); // Ensures text has enough read area
const imgHeight = (originalImg.height / originalImg.width) * imgWidth;
const padding = 40;
const gap = 40;
const colWidth = (imgWidth - (padding * 2) - gap) / 2;
const fontFamily = '"Segoe UI", Roboto, Helvetica, Arial, sans-serif';
// Helper to wrap text into array of lines based on fixed width
const wrapText = (context, text, maxWidth) => {
const lines = [];
const paragraphs = text.split('\n');
for (let p of paragraphs) {
if (p.trim() === '') {
lines.push('');
continue;
}
const words = p.split(' ');
let currentLine = words[0];
for (let i = 1; i < words.length; i++) {
const word = words[i];
const measureTextWidth = context.measureText(currentLine + " " + word).width;
if (measureTextWidth < maxWidth) {
currentLine += " " + word;
} else {
lines.push(currentLine);
currentLine = word;
}
}
lines.push(currentLine);
}
return lines;
};
// Prepare text measurement
ctx.font = `16px ${fontFamily}`;
const origLines = wrapText(ctx, extractedText, colWidth - 40);
const transLines = wrapText(ctx, translatedText, colWidth - 40);
const lineHeight = 26;
const textHeightOrig = origLines.length * lineHeight;
const textHeightTrans = transLines.length * lineHeight;
const textSectionHeight = Math.max(textHeightOrig, textHeightTrans, 80) + 120; // 120 for padding & headers
canvas.width = imgWidth;
canvas.height = imgHeight + textSectionHeight;
// Draw background
ctx.fillStyle = '#f8f9fa';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Draw Original Image
ctx.drawImage(originalImg, 0, 0, imgWidth, imgHeight);
// Draw separator bar and branding
ctx.fillStyle = '#0d6efd';
ctx.fillRect(0, imgHeight, canvas.width, 8);
ctx.fillStyle = '#adb5bd';
ctx.font = `italic 14px ${fontFamily}`;
ctx.textAlign = 'right';
ctx.fillText('AI Powered Image Translator Content', canvas.width - 20, imgHeight + 35);
ctx.textAlign = 'left';
// Text Section positioning
const origX = padding;
const transX = padding + colWidth + gap;
const startY = imgHeight + 60;
// Draw Column Headers
ctx.fillStyle = '#212529';
ctx.font = `bold 22px ${fontFamily}`;
ctx.fillText(`Original Text (${sourceLangISO.toUpperCase()})`, origX, startY);
ctx.fillText(`Translated Text (${targetLangISO.toUpperCase()})`, transX, startY);
// Draw Content Boxes (white pane with slight drop shadow & border)
const boxY = startY + 20;
const boxHeight = textSectionHeight - 100;
ctx.fillStyle = '#ffffff';
ctx.shadowColor = 'rgba(0,0,0,0.05)';
ctx.shadowBlur = 10;
ctx.shadowOffsetY = 4;
ctx.fillRect(origX, boxY, colWidth, boxHeight);
ctx.fillRect(transX, boxY, colWidth, boxHeight);
// Reset shadow & draw border
ctx.shadowColor = 'transparent';
ctx.shadowBlur = 0;
ctx.shadowOffsetY = 0;
ctx.strokeStyle = '#dee2e6';
ctx.lineWidth = 1;
ctx.strokeRect(origX, boxY, colWidth, boxHeight);
ctx.strokeRect(transX, boxY, colWidth, boxHeight);
// Render inner text lines
ctx.fillStyle = '#495057';
ctx.font = `16px ${fontFamily}`;
const textStartY = startY + 60; // Start lines padded from box top
for (let i = 0; i < origLines.length; i++) {
ctx.fillText(origLines[i], origX + 20, textStartY + (i * lineHeight));
}
for (let i = 0; i < transLines.length; i++) {
ctx.fillText(transLines[i], transX + 20, textStartY + (i * lineHeight));
}
// Return the generated image utility canvas
return canvas;
}
Apply Changes