You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, searchText = "", highlightColor = "rgba(255, 255, 0, 0.5)") {
// 1. Create a responsive container
const container = document.createElement('div');
container.style.display = 'flex';
container.style.flexDirection = 'column';
container.style.alignItems = 'center';
container.style.fontFamily = 'Arial, sans-serif';
container.style.width = '100%';
container.style.boxSizing = 'border-box';
// 2. Setup the canvas for drawing the image and highlights
const canvas = document.createElement('canvas');
canvas.width = originalImg.naturalWidth || originalImg.width;
canvas.height = originalImg.naturalHeight || originalImg.height;
canvas.style.maxWidth = '100%';
canvas.style.height = 'auto';
canvas.style.border = '1px solid #ccc';
canvas.style.borderRadius = '6px';
canvas.style.boxShadow = '0 2px 8px rgba(0,0,0,0.1)';
const ctx = canvas.getContext('2d');
ctx.drawImage(originalImg, 0, 0);
container.appendChild(canvas);
// 3. Setup a status/results panel below the canvas
const statusPanel = document.createElement('div');
statusPanel.style.marginTop = '20px';
statusPanel.style.padding = '15px 20px';
statusPanel.style.borderRadius = '6px';
statusPanel.style.backgroundColor = '#f8f9fa';
statusPanel.style.width = '100%';
statusPanel.style.boxSizing = 'border-box';
statusPanel.style.border = '1px solid #dee2e6';
statusPanel.style.boxShadow = '0 1px 3px rgba(0,0,0,0.05)';
const statusText = document.createElement('h4');
statusText.style.margin = '0 0 10px 0';
statusText.style.color = '#495057';
statusText.style.fontWeight = '600';
statusText.textContent = 'Initializing AI OCR Engine... ⚙️';
statusPanel.appendChild(statusText);
const resultsList = document.createElement('ul');
resultsList.style.margin = '0';
resultsList.style.paddingLeft = '20px';
resultsList.style.color = '#333';
resultsList.style.fontSize = '14px';
resultsList.style.display = 'none'; // Hidden until search yields results
statusPanel.appendChild(resultsList);
container.appendChild(statusPanel);
// 4. Run Optical Character Recognition asynchronously
(async () => {
try {
// Dynamically load Tesseract.js 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);
});
}
statusText.textContent = 'Scanning image for text... 🔍';
// Execute OCR recognition
const worker = await window.Tesseract.createWorker('eng');
const { data } = await worker.recognize(canvas);
await worker.terminate();
const lines = data.lines || [];
const target = searchText.trim().toLowerCase();
ctx.lineWidth = Math.max(1, Math.floor(canvas.width / 500) * 2);
ctx.strokeStyle = "rgba(230, 57, 70, 0.9)"; // Red bounding box around matches
let matchCount = 0;
// Iterate over all text lines identified by Tesseract
for (const line of lines) {
const textLower = line.text.toLowerCase();
// If user provided a search phrase and the line contains it
if (target.length > 0 && textLower.includes(target)) {
matchCount++;
// If target contains spaces, highlight the whole matching line bounds
if (target.includes(" ")) {
ctx.fillStyle = highlightColor;
ctx.fillRect(line.bbox.x0, line.bbox.y0, line.bbox.x1 - line.bbox.x0, line.bbox.y1 - line.bbox.y0);
ctx.strokeRect(line.bbox.x0, line.bbox.y0, line.bbox.x1 - line.bbox.x0, line.bbox.y1 - line.bbox.y0);
} else {
// For single words, highlight specific matching words precisely
for (const word of line.words) {
if (word.text.toLowerCase().includes(target)) {
ctx.fillStyle = highlightColor;
ctx.fillRect(word.bbox.x0, word.bbox.y0, word.bbox.x1 - word.bbox.x0, word.bbox.y1 - word.bbox.y0);
ctx.strokeRect(word.bbox.x0, word.bbox.y0, word.bbox.x1 - word.bbox.x0, word.bbox.y1 - word.bbox.y0);
}
}
}
// Append matching text context to the list
const li = document.createElement('li');
li.style.marginBottom = '6px';
li.style.lineHeight = '1.5';
// XSS-Safe HTML entity encoding
const escapeSpan = document.createElement('span');
escapeSpan.textContent = line.text.trim();
const safeText = escapeSpan.innerHTML;
// Highlight phrase in the text result view
const escapedTarget = target.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const regex = new RegExp(`(${escapedTarget})`, 'gi');
li.innerHTML = safeText.replace(regex, '<strong style="background-color: #ffeb3b; color: #000; padding: 2px 4px; border-radius: 3px; box-shadow: 0 1px 2px rgba(0,0,0,0.15); font-weight: bold;">$1</strong>');
resultsList.appendChild(li);
}
// If search phrase is empty, draw faint highlights over all detected text areas
else if (target.length === 0) {
ctx.fillStyle = "rgba(100, 200, 100, 0.15)";
ctx.fillRect(line.bbox.x0, line.bbox.y0, line.bbox.x1 - line.bbox.x0, line.bbox.y1 - line.bbox.y0);
}
}
// Update status UI based on results found
if (target.length > 0) {
if (matchCount > 0) {
statusText.textContent = `Found "${searchText}" in ${matchCount} block(s). ✨`;
statusText.style.color = '#28a745'; // Green success color
statusPanel.style.borderColor = '#c3e6cb';
statusPanel.style.backgroundColor = '#d4edda';
resultsList.style.display = 'block';
} else {
statusText.textContent = `No matches found for "${searchText}". ❌`;
statusText.style.color = '#dc3545'; // Red error color
statusPanel.style.backgroundColor = '#f8d7da';
statusPanel.style.borderColor = '#f5c6cb';
}
} else {
if (lines.length > 0) {
statusText.textContent = `Scanned ${lines.length} lines of text. Pass a search string via parameters to find topics. ✅`;
} else {
statusText.textContent = `OCR complete. No text detected in this image. ❕`;
}
}
} catch(err) {
// Handle and display errors properly
statusText.textContent = 'Error processing image: ' + err.message;
statusText.style.color = '#dc3545';
statusPanel.style.backgroundColor = '#f8d7da';
statusPanel.style.borderColor = '#f5c6cb';
}
})();
// Synchronously return the DOM container (the OCR runs natively in the background)
return container;
}
Apply Changes