(function () { const $ = (s, c) => (c || document).querySelector(s), $$ = (s, c) => (c || document).querySelectorAll(s), O = $('#ocr-svg-overlay'), I = $('#ocr-svg-image'), P = $('#canvas-placeholder-msg'), D = $('#details-content-area'), V = $('.canvas-viewport-container'), W = $('.viewport-wrapper'), Z = $('#zoom-level-text'), btn = id => $(`#btn-zoom-${id}`); let z = 1, px = 0, py = 0, dr = 0, dragging = false, sx = 0, sy = 0; const upd = () => { W && (W.style.transform = `translate(${px}px,${py}px) scale(${z})`); Z && (Z.innerText = `${Math.round(z * 100)}%`); }, zoom = f => { z = Math.min(5, Math.max(.5, z * f)); upd(); }, reset = () => { z = 1; px = 0; py = 0; upd(); }; [['In', () => zoom(1.2)], ['Out', () => zoom(1 / 1.2)], ['Reset', reset]].forEach(([id, fn]) => { const b = btn(id.toLowerCase()); b && b.addEventListener('click', e => { e.stopPropagation(); fn(); }); }); V && (I.addEventListener('dragstart', e => e.preventDefault()), V.addEventListener('wheel', e => { e.preventDefault(); zoom(e.deltaY < 0 ? 1.15 : .85); }, { passive: !1 }), V.addEventListener('mousedown', e => { if (e.target.closest('button,input')) return; dragging = !0; dr = 0; sx = e.clientX - px; sy = e.clientY - py; V.style.cursor = 'grabbing'; e.preventDefault(); }), window.addEventListener('mousemove', e => { if (!dragging) return; const dx = e.clientX - (sx + px), dy = e.clientY - (sy + py); dr += Math.sqrt(dx * dx + dy * dy); px = e.clientX - sx; py = e.clientY - sy; upd(); }), window.addEventListener('mouseup', () => { dragging && (dragging = !1, V.style.cursor = 'grab'); })); const src = () => { const c = $('#input-image'); return c && c.querySelector('img')?.src || null; }, CELL = (k, v, c = 'text-foreground') => `${k}${v}`, EMPTY = '

Click any element inside the canvas to inspect its properties

'; function populate(data) { O.innerHTML = ''; if (!data) return; const el = []; ['blocks', 'paragraphs'].forEach(k => (data[k] || []).forEach(v => el.push({ t: k == 'blocks' ? 'Block' : 'Paragraph', d: v }))); (data.lines || []).forEach(l => { el.push({ t: 'Line', d: l }); (l.words || []).forEach(w => { el.push({ t: 'Word', d: w }); (w.symbols || []).forEach(s => el.push({ t: 'Symbol', d: s })); w.whitespace_bounding_box && el.push({ t: 'Whitespace', d: { bounding_box: w.whitespace_bounding_box } }); }); }); const order = ['Block', 'Paragraph', 'Line', 'Word', 'Symbol', 'Whitespace']; el.sort((a, b) => order.indexOf(a.t) - order.indexOf(b.t)); window.ocr = el; el.forEach((e, i) => { const b = e.d.bounding_box; if (!b) return; const r = document.createElementNS('http://www.w3.org/2000/svg', 'rect'); r.setAttribute('class', `ocr-box box-${e.t.toLowerCase()}`); r.setAttribute('x', b.x); r.setAttribute('y', b.y); r.setAttribute('width', b.width); r.setAttribute('height', b.height); b.angle && r.setAttribute('transform', `rotate(${b.angle},${b.x + b.width / 2},${b.y + b.height / 2})`); r.setAttribute('data-idx', i); O.appendChild(r); }); } const C = { Block: ['#f472b6', 'bg-pink-500/20 text-pink-400'], Paragraph: ['#22d3ee', 'bg-cyan-500/20 text-cyan-400'], Line: ['#c084fc', 'bg-violet-500/20 text-violet-400'], Word: ['#34d399', 'bg-emerald-500/20 text-emerald-400'], Symbol: ['#fbbf24', 'bg-amber-500/20 text-amber-400'], Whitespace: ['#f87171', 'bg-red-500/20 text-red-400'] }; function meta(item) { const d = item.d, b = d.bounding_box, t = item.t, cl = (C[t] || ['#818cf8', 'bg-indigo-500/20 text-indigo-400'])[1]; let rows = [CELL('Type', `${t}`), CELL('Confidence', d.confidence !== void 0 ? (d.confidence * 100).toFixed(1) + '%' : 'N/A', 'text-emerald-400')]; if (t == 'Line' || t == 'Word') rows.push(CELL('Language', d.language || 'N/A'), CELL('Direction', d.direction || 'N/A'), CELL('Content Type', d.content_type || 'N/A')); if (t == 'Word') { const f = d.foreground_rgb_hex || 'N/A', g = d.background_rgb_hex || 'N/A'; rows.push(CELL('FG Color', `${f}`), CELL('BG Color', `${g}`), CELL('Gray', `${d.foreground_gray_value} / ${d.background_gray_value}`), CELL('Space After', d.has_space_after ? 'Yes' : 'No')); } rows.push(CELL('BBox', `${b.x}, ${b.y}, ${b.width}×${b.height}`), CELL('Rotation', `${(b.angle || 0).toFixed(1)}°`)); D.innerHTML = `
${rows.map(r => `
${r}
`).join('')}
Extracted text
${d.utf8_string || 'N/A'}
`; } O.addEventListener('click', e => { if (dr > 5 || !window.ocr) return; const el = e.target.closest('.ocr-box'); if (!el) return; $$('.ocr-box.selected').forEach(x => x.classList.remove('selected')); el.classList.add('selected'); const item = window.ocr[+el.getAttribute('data-idx')]; item && meta(item); }); function loadImg(src) { reset(); if (!src) { I.removeAttribute('src'); O.innerHTML = ''; P.style.display = 'block'; D.innerHTML = EMPTY; return; } P.style.display = 'none'; I.onload = () => O.setAttribute('viewBox', `0 0 ${I.naturalWidth || I.width} ${I.naturalHeight || I.height}`); I.src = src; } window.loadStreamedData = function (raw) { if (!raw || !raw.trim()) { loadImg(null); I.style.filter = 'none'; return; } try { const p = JSON.parse(raw); I.style.filter = 'none'; P.style.display = 'none'; D.innerHTML = EMPTY; populate(p.annotation); loadImg(src()); } catch (e) { console.error(e); } }; (function init() { const el = $('#input-image'); if (!el) { setTimeout(init, 150); return; } new MutationObserver(() => { const s = src(); if (s && s !== I.src) { window.ocr = null; loadImg(s); I.style.filter = 'blur(8px)'; } else if (!s) { loadImg(null); I.style.filter = 'none'; } }).observe(el, { childList: true, subtree: true, attributes: true }); const s = src(); s && loadImg(s); })(); $$('.canvas-toggles input').forEach(el => { const fn = () => O.classList.toggle('show-' + el.id.replace('cb-', ''), el.checked); el.addEventListener('change', fn); fn(); }); })();