You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, targetLang = 'es', sourceLang = 'eng') {
/**
* Dynamically loads a script if it's not already present in the DOM.
* @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 (document.querySelector(`script[src="${url}"]`)) {
resolve();
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);
});
};
// A simple mock dictionary for translation. A real app would use an API.
const mockDictionary = {
'es': {
'hello': 'hola',
'world': 'mundo',
'open': 'abrir',
'close': 'cerrar',
'exit': 'salida',
'warning': 'advertencia',
'danger': 'peligro',
'text': 'texto'
},
'fr': {
'hello': 'bonjour',
'world': 'monde',
'open': 'ouvrir',
'close': 'fermer',
'exit': 'sortie',
'warning': 'avertissement',
'danger': 'danger',
'text': 'texte'
},
'de': {
'hello': 'hallo',
'world': 'welt',
'open': 'öffnen',
'close': 'schließen',
'exit': 'ausfahrt',
'warning': 'warnung',
'danger': 'gefahr',
'text': 'text'
}
};
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true });
canvas.width = originalImg.naturalWidth;
canvas.height = originalImg.naturalHeight;
ctx.drawImage(originalImg, 0, 0);
// --- 1. Load OCR Library ---
// Using Tesseract.js, a popular client-side OCR library.
// The first execution will be slow as it downloads the library and language data.
try {
await loadScript('https://cdn.jsdelivr.net/npm/tesseract.js@5/dist/tesseract.min.js');
} catch (error) {
console.error("Failed to load Tesseract.js:", error);
ctx.fillStyle = "red";
ctx.font = "20px Arial";
ctx.fillText("Error: Could not load the OCR library.", 10, 30);
return canvas;
}
// --- 2. Perform OCR ---
const loadingMessage = document.createElement('div');
loadingMessage.style.fontFamily = "Arial, sans-serif";
loadingMessage.innerText = "Initializing OCR and processing image... This may take a moment.";
// Create a temporary container to show progress
const tempContainer = document.createElement('div');
tempContainer.style.position = 'relative';
const progressCanvas = canvas.cloneNode();
const pCtx = progressCanvas.getContext('2d');
pCtx.fillStyle = 'rgba(0,0,0,0.5)';
pCtx.fillRect(0,0,progressCanvas.width, progressCanvas.height);
pCtx.fillStyle = 'white';
pCtx.font = `${Math.min(progressCanvas.width/20, 30)}px Arial`;
pCtx.textAlign = 'center';
pCtx.textBaseline = 'middle';
pCtx.fillText("Processing...", progressCanvas.width/2, progressCanvas.height/2);
tempContainer.appendChild(originalImg.cloneNode());
tempContainer.appendChild(progressCanvas);
progressCanvas.style.position = 'absolute';
progressCanvas.style.left = '0';
progressCanvas.style.top = '0';
const worker = await Tesseract.createWorker(sourceLang, 1, {
logger: m => console.log(m) // Log OCR progress
});
const { data: { lines } } = await worker.recognize(canvas);
// --- 3. Process Each Detected Line of Text ---
for (const line of lines) {
const bbox = line.bbox;
const originalText = line.text.trim().toLowerCase();
// --- 3a. Mock Translation ---
const dictionary = mockDictionary[targetLang] || {};
const words = originalText.split(/\s+/);
const translatedWords = words.map(word => dictionary[word] || `[${word}]`);
const translatedText = translatedWords.join(' ');
if (originalText.length === 0) continue;
// --- 3b. Inpainting (Covering old text) ---
// We'll calculate the average color around the bounding box to fill it.
const border = Math.max(2, Math.floor(bbox.height * 0.1));
let r = 0, g = 0, b = 0, count = 0;
const samplePoints = [
{ x: bbox.x0 - border, y: bbox.y0 - border },
{ x: bbox.x1 + border, y: bbox.y0 - border },
{ x: bbox.x0 - border, y: bbox.y1 + border },
{ x: bbox.x1 + border, y: bbox.y1 + border },
{ x: bbox.x0 + bbox.width / 2, y: bbox.y0 - border },
{ x: bbox.x0 + bbox.width / 2, y: bbox.y1 + border },
{ x: bbox.x0 - border, y: bbox.y0 + bbox.height / 2 },
{ x: bbox.x1 + border, y: bbox.y0 + bbox.height / 2 },
];
samplePoints.forEach(p => {
if (p.x >= 0 && p.x < canvas.width && p.y >= 0 && p.y < canvas.height) {
const pixel = ctx.getImageData(p.x, p.y, 1, 1).data;
r += pixel[0];
g += pixel[1];
b += pixel[2];
count++;
}
});
if (count > 0) {
ctx.fillStyle = `rgb(${Math.round(r/count)}, ${Math.round(g/count)}, ${Math.round(b/count)})`;
ctx.fillRect(bbox.x0, bbox.y0, bbox.width, bbox.height);
}
// --- 3c. Render New Text ---
// Determine if background is light or dark for text color
const bgColorLuminance = (0.299 * (r/count) + 0.587 * (g/count) + 0.114 * (b/count)) / 255;
ctx.fillStyle = bgColorLuminance > 0.5 ? 'black' : 'white';
// Attempt to fit the text inside the original bounding box
let fontSize = bbox.height * 0.8;
ctx.font = `${fontSize}px Arial`;
ctx.textAlign = 'left';
ctx.textBaseline = 'top';
let textWidth = ctx.measureText(translatedText).width;
while(textWidth > bbox.width && fontSize > 8) {
fontSize -= 1;
ctx.font = `${fontSize}px Arial`;
textWidth = ctx.measureText(translatedText).width;
}
const x = bbox.x0;
const y = bbox.y0 + (bbox.height - fontSize) / 2; // Center vertically
ctx.fillText(translatedText, x, y);
}
// --- 4. Cleanup ---
await worker.terminate();
return canvas;
}
Apply Changes