{"id":30416,"date":"2025-09-15T17:06:15","date_gmt":"2025-09-15T09:06:15","guid":{"rendered":"https:\/\/www.hkmu.edu.hk\/oetools\/?page_id=30416"},"modified":"2025-11-03T17:08:18","modified_gmt":"2025-11-03T09:08:18","slug":"agent-7-ocr","status":"publish","type":"page","link":"https:\/\/www.hkmu.edu.hk\/oetools\/agent-7-ocr\/","title":{"rendered":"Agent 7 OCR"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"30416\" class=\"elementor elementor-30416\" data-elementor-settings=\"[]\">\n\t\t\t\t\t\t\t<div class=\"elementor-section-wrap\">\n\t\t\t\t\t\t\t<section class=\"has_eae_slider wavo-column-gap-default elementor-section elementor-top-section elementor-element elementor-element-017dbc0 elementor-section-boxed elementor-section-height-default elementor-section-height-default\" data-id=\"017dbc0\" data-element_type=\"section\" data-settings=\"{&quot;jet_parallax_layout_list&quot;:[]}\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"has_eae_slider elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-8223882\" data-id=\"8223882\" data-element_type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t\t\t<div class=\"elementor-element elementor-element-08e7119 elementor-widget elementor-widget-html\" data-id=\"08e7119\" data-element_type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t<!DOCTYPE html>\r\n<html lang=\"en\">\r\n<head>\r\n    <meta charset=\"UTF-8\">\r\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n    <title>Instant OCR Tool<\/title>\r\n    <script src=\"https:\/\/cdn.jsdelivr.net\/npm\/pdfjs-dist@3.6.172\/build\/pdf.min.js\"><\/script>\r\n    <script src=\"https:\/\/cdn.jsdelivr.net\/npm\/tesseract.js@4\/dist\/tesseract.min.js\"><\/script>\r\n    <style>\r\n        \/* ================ GLOBAL STYLES ================ *\/\r\n        :root {\r\n            --primary-color: #fa975e;\r\n            --primary-light: #e8864a;\r\n            --primary-dark: #d87a40;\r\n            --primary-blue: #6495ED;\r\n            --accent-color: #fa975e;\r\n            --accent-green: #7AC143;\r\n            --light-gray: #f9f5f3;\r\n            --medium-gray: #e0e0e0;\r\n            --dark-gray: #707070;\r\n            --success-color: #7AC143;\r\n            --danger-color: #f44336;\r\n            --warning-color: #ff9800;\r\n            --info-color: #fa975e;\r\n            --primary-red: #e53e3e;\r\n            --accent-orange: #ed8936;\r\n            --dark-text: #333;\r\n            --light-text: #666;\r\n            --card-bg: rgba(255, 255, 255, 0.95);\r\n            --bg-light: linear-gradient(135deg, #fa975e, #e8864a);\r\n            --shadow: 0 15px 40px rgba(0, 0, 0, 0.2);\r\n        }\r\n\r\n        * {\r\n            box-sizing: border-box;\r\n            margin: 0;\r\n            padding: 0;\r\n        }\r\n\r\n        body {\r\n            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;\r\n            background: var(--bg-light);\r\n            color: var(--dark-text);\r\n            line-height: 1.6;\r\n            padding: 20px;\r\n            min-height: 100vh;\r\n        }\r\n\r\n        .container {\r\n            max-width: 1800px;\r\n            margin: 0 auto;\r\n            background: var(--card-bg);\r\n            border-radius: 15px;\r\n            box-shadow: var(--shadow);\r\n            overflow: hidden;\r\n            padding: 10px;\r\n        }\r\n\r\n        \/* ================ HEADER STYLES ================ *\/\r\n        header {\r\n            text-align: center;\r\n            margin-bottom: 30px;\r\n            padding: 20px 0;\r\n            background: linear-gradient(45deg, var(--primary-color), var(--primary-light));\r\n            position: relative;\r\n            overflow: hidden;\r\n            border-radius: 15px;\r\n        }\r\n\r\n        header::before {\r\n            content: \"\";\r\n            position: absolute;\r\n            top: -50%;\r\n            left: -50%;\r\n            width: 200%;\r\n            height: 200%;\r\n            background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0) 70%);\r\n            transform: rotate(30deg);\r\n        }\r\n\r\n        h1 {\r\n            color: #fff;\r\n            font-size: 2.5rem;\r\n            margin-bottom: 10px;\r\n            text-shadow: 0 2px 4px rgba(0,0,0,0.3);\r\n            position: relative;\r\n            z-index: 1;\r\n        }\r\n\r\n        .subtitle {\r\n            color: #fff;\r\n            font-size: 1.1rem;\r\n            max-width: 800px;\r\n            margin: 0 auto;\r\n            opacity: 0.9;\r\n            position: relative;\r\n            z-index: 1;\r\n        }\r\n\r\n        \/* ================ CARD STYLES ================ *\/\r\n        .card {\r\n            background: var(--card-bg);\r\n            border-radius: 12px;\r\n            box-shadow: var(--shadow);\r\n            padding: 25px;\r\n            margin-bottom: 25px;\r\n            transition: all 0.3s ease;\r\n        }\r\n\r\n        .card:hover {\r\n            transform: translateY(-3px);\r\n            box-shadow: 0 8px 25px rgba(0,0,0,0.15);\r\n        }\r\n\r\n        .card-title, .output-header h3 {\r\n            font-size: 1.5rem;\r\n            color: var(--primary-color);\r\n            margin-bottom: 20px;\r\n            padding-bottom: 10px;\r\n            border-bottom: 2px solid var(--light-gray);\r\n            font-weight: 700;\r\n            display: flex;\r\n            align-items: center;\r\n            gap: 10px;\r\n        }\r\n\r\n        \/* ================ INPUT SECTION ================ *\/\r\n        .input-section {\r\n            display: grid;\r\n            grid-template-columns: 1fr 200px;\r\n            gap: 20px;\r\n            align-items: end;\r\n            margin-bottom: 20px;\r\n        }\r\n\r\n        @media (max-width: 768px) {\r\n            .input-section {\r\n                grid-template-columns: 1fr;\r\n            }\r\n        }\r\n\r\n        \/* ================ ENHANCED SELECT STYLING ================ *\/\r\n        .form-group {\r\n            margin-bottom: 20px;\r\n        }\r\n\r\n        .form-group label {\r\n            display: block;\r\n            margin-bottom: 8px;\r\n            font-weight: 600;\r\n            color: var(--dark-text);\r\n            font-size: 1.1rem;\r\n        }\r\n\r\n        select {\r\n            width: 100%;\r\n            padding: 12px 16px;\r\n            border: 2px solid var(--medium-gray);\r\n            border-radius: 8px;\r\n            font-size: 16px;\r\n            background: white;\r\n            color: var(--dark-text);\r\n            cursor: pointer;\r\n            transition: all 0.2s ease;\r\n            appearance: none;\r\n            background-image: url('data:image\/svg+xml;charset=US-ASCII,<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 4 5\"><path fill=\"%23666\" d=\"m0 1 2 2 2-2z\"\/><\/svg>');\r\n            background-repeat: no-repeat;\r\n            background-position: right 12px center;\r\n            background-size: 12px;\r\n        }\r\n\r\n        select:focus {\r\n            outline: none;\r\n            border-color: var(--primary-color);\r\n            box-shadow: 0 0 0 3px rgba(250, 151, 94, 0.2);\r\n        }\r\n\r\n        select:hover {\r\n            border-color: var(--primary-light);\r\n        }\r\n\r\n        \/* Style for optgroups *\/\r\n        optgroup {\r\n            font-weight: 600;\r\n            color: var(--primary-color);\r\n            background-color: var(--light-gray);\r\n            font-style: normal;\r\n        }\r\n\r\n        option {\r\n            padding: 8px;\r\n            background-color: white;\r\n            color: var(--dark-text);\r\n        }\r\n\r\n        option:hover, option:focus {\r\n            background-color: var(--light-gray);\r\n        }\r\n\r\n        \/* ================ FILE UPLOAD ================ *\/\r\n        .file-upload {\r\n            padding: 30px 20px;\r\n            border: 3px dashed var(--primary-color);\r\n            border-radius: 12px;\r\n            text-align: center;\r\n            cursor: pointer;\r\n            transition: all 0.3s;\r\n            background: rgba(250, 151, 94, 0.05);\r\n            margin-bottom: 20px;\r\n        }\r\n\r\n        .file-upload:hover {\r\n            border-color: var(--primary-light);\r\n            background-color: rgba(232, 134, 74, 0.15);\r\n        }\r\n\r\n        .file-upload.dragover {\r\n            border-color: var(--accent-green);\r\n            background-color: rgba(122, 193, 67, 0.1);\r\n        }\r\n\r\n        .file-upload input {\r\n            display: none;\r\n        }\r\n\r\n        .file-upload label {\r\n            display: block;\r\n            cursor: pointer;\r\n            font-weight: 600;\r\n            color: var(--primary-color);\r\n            font-size: 1.2rem;\r\n            margin-bottom: 10px;\r\n        }\r\n\r\n        .file-info {\r\n            font-size: 14px;\r\n            color: var(--light-text);\r\n            margin-top: 10px;\r\n        }\r\n\r\n        .accepted-formats {\r\n            font-size: 12px;\r\n            color: var(--light-text);\r\n            margin-top: 5px;\r\n        }\r\n\r\n        \/* ================ BUTTON STYLES ================ *\/\r\n        .button-group {\r\n            display: flex;\r\n            justify-content: center;\r\n            gap: 15px;\r\n            margin-bottom: 20px;\r\n        }\r\n\r\n        button {\r\n            padding: 8px 28px;\r\n            border: none;\r\n            border-radius: 8px;\r\n            cursor: pointer;\r\n            font-size: 24px;\r\n            font-weight: 600;\r\n            transition: all 0.3s ease;\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: center;\r\n            gap: 8px;\r\n            min-width: 120px;\r\n        }\r\n\r\n        #convertBtn {\r\n            background-color: var(--accent-green);\r\n            color: white;\r\n            box-shadow: 0 4px 8px rgba(122, 193, 67, 0.3);\r\n        }\r\n\r\n        #convertBtn:hover:not(:disabled) {\r\n            background-color: #5a8f32;\r\n            transform: translateY(-2px);\r\n            box-shadow: 0 6px 12px rgba(122, 193, 67, 0.4);\r\n        }\r\n\r\n        #convertBtn:disabled {\r\n            background-color: var(--medium-gray);\r\n            cursor: not-allowed;\r\n            transform: none;\r\n            box-shadow: none;\r\n        }\r\n\r\n        .copy-btn, .download-btn {\r\n            background-color: var(--primary-color);\r\n            color: white;\r\n            box-shadow: 0 4px 8px rgba(250, 151, 94, 0.3);\r\n        }\r\n\r\n        .copy-btn:hover, .download-btn:hover {\r\n            background-color: var(--primary-dark);\r\n            transform: translateY(-2px);\r\n            box-shadow: 0 6px 12px rgba(250, 151, 94, 0.4);\r\n        }\r\n\r\n        .enhance-btn {\r\n            background-color: var(--accent-green);\r\n            color: white;\r\n            padding: 8px 28px;\r\n            font-size: 24px;\r\n            box-shadow: 0 4px 8px rgba(122, 193, 67, 0.3);\r\n        }\r\n\r\n        .enhance-btn:hover:not(:disabled) {\r\n            background-color: #5a8f32;\r\n            transform: translateY(-2px);\r\n            box-shadow: 0 6px 12px rgba(122, 193, 67, 0.4);\r\n        }\r\n\r\n        .enhance-btn:disabled {\r\n            background-color: var(--medium-gray);\r\n            cursor: not-allowed;\r\n            transform: none;\r\n            box-shadow: none;\r\n        }\r\n\r\n        \/* ================ STATUS & PROGRESS ================ *\/\r\n        .status {\r\n            margin-top: 15px;\r\n            margin-bottom: 15px;\r\n            padding: 12px;\r\n            border-radius: 8px;\r\n            font-weight: 500;\r\n            border: 1px solid transparent;\r\n            text-align: center;\r\n            display: none;\r\n        }\r\n\r\n        .status.info {\r\n            background-color: #fff3cd;\r\n            color: #856404;\r\n            border-color: #ffeaa7;\r\n            display: block;\r\n        }\r\n\r\n        .status.error {\r\n            background-color: #f8d7da;\r\n            color: #721c24;\r\n            border-color: #f5c6cb;\r\n            display: block;\r\n        }\r\n\r\n        .status.success {\r\n            background-color: #d4edda;\r\n            color: #155724;\r\n            border-color: #c3e6cb;\r\n            display: block;\r\n        }\r\n\r\n        .spinner {\r\n            border: 3px solid rgba(0, 0, 0, 0.1);\r\n            border-top: 3px solid var(--primary-color);\r\n            border-radius: 50%;\r\n            width: 20px;\r\n            height: 20px;\r\n            animation: spin 1s linear infinite;\r\n            display: inline-block;\r\n            margin-right: 10px;\r\n        }\r\n\r\n        @keyframes spin {\r\n            0% { transform: rotate(0deg); }\r\n            100% { transform: rotate(360deg); }\r\n        }\r\n\r\n        \/* ================ OUTPUT SECTION ================ *\/\r\n        .output-section {\r\n            display: none;\r\n        }\r\n\r\n        .output-section.show {\r\n            display: block;\r\n        }\r\n\r\n        .output-header {\r\n            display: flex;\r\n            justify-content: space-between;\r\n            align-items: center;\r\n            margin-bottom: 15px;\r\n        }\r\n\r\n        .output-controls {\r\n            display: flex;\r\n            gap: 10px;\r\n        }\r\n\r\n        #outputText, #aiOutputText {\r\n            width: 100%;\r\n            min-height: 300px;\r\n            padding: 15px;\r\n            border: 2px solid var(--medium-gray);\r\n            border-radius: 8px;\r\n            resize: vertical;\r\n            font-size: 14px;\r\n            font-family: 'Courier New', monospace;\r\n            background: #f9fbfd;\r\n            color: var(--dark-text);\r\n        }\r\n\r\n        #outputText:focus, #aiOutputText:focus {\r\n            outline: none;\r\n            border-color: var(--primary-color);\r\n            box-shadow: 0 0 0 3px rgba(250, 151, 94, 0.2);\r\n        }\r\n\r\n        .search-container {\r\n            margin-bottom: 15px;\r\n            display: flex;\r\n            gap: 10px;\r\n            align-items: center;\r\n        }\r\n\r\n        #searchInput, #aiSearchInput {\r\n            flex: 1;\r\n            padding: 8px 12px;\r\n            border: 2px solid var(--medium-gray);\r\n            border-radius: 6px;\r\n            font-size: 14px;\r\n        }\r\n\r\n        #searchInput:focus, #aiSearchInput:focus {\r\n            outline: none;\r\n            border-color: var(--primary-color);\r\n        }\r\n\r\n        .search-info {\r\n            font-size: 12px;\r\n            color: var(--light-text);\r\n            margin-left: 10px;\r\n        }\r\n\r\n        \/* ================ ACKNOWLEDGEMENT ================ *\/\r\n        .acknowledgement {\r\n            text-align: center;\r\n            margin-top: 40px;\r\n            padding-top: 20px;\r\n            color: var(--light-text);\r\n            font-size: 0.9rem;\r\n            border-top: 1px solid var(--light-gray);\r\n        }\r\n\r\n        .acknowledgement a {\r\n            color: var(--primary-light);\r\n            text-decoration: none;\r\n            font-weight: 600;\r\n        }\r\n\r\n        .acknowledgement a:hover {\r\n            text-decoration: underline;\r\n        }\r\n    <\/style>\r\n<\/head>\r\n<body>\r\n    <div class=\"container\">\r\n        <header>\r\n            <h1>Instant OCR Tool<\/h1>\r\n            <p class=\"subtitle\">Extract text accurately from PDFs and images (PNG, JPG, GIF, BMP, TIFF) with 95 language options. Refine results instantly with one\u2011click AI cleanup for pristine output.<\/p>\r\n        <\/header>\r\n\r\n        <div class=\"card\">\r\n            <h2 class=\"card-title\">File Upload & OCR Settings<\/h2>\r\n            \r\n\t\t\t<div class=\"input-section\">\r\n\t\t\t\t<div class=\"form-group\">\r\n\t\t\t\t\t<label for=\"languageSelect\">OCR Language:<\/label>\r\n\t\t\t\t\t<select id=\"languageSelect\">\r\n\t\t\t\t\t\t<optgroup label=\"Most Common\">\r\n\t\t\t\t\t\t\t<option value=\"eng\">English<\/option>\r\n\t\t\t\t\t\t\t<option value=\"chi_sim\">Chinese (Simplified)<\/option>\r\n\t\t\t\t\t\t\t<option value=\"chi_tra\">Chinese (Traditional)<\/option>\r\n\t\t\t\t\t\t\t<option value=\"spa\">Spanish<\/option>\r\n\t\t\t\t\t\t\t<option value=\"fra\">French<\/option>\r\n\t\t\t\t\t\t\t<option value=\"deu\">German<\/option>\r\n\t\t\t\t\t\t\t<option value=\"jpn\">Japanese<\/option>\r\n\t\t\t\t\t\t\t<option value=\"rus\">Russian<\/option>\r\n\t\t\t\t\t\t<\/optgroup>\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\t<optgroup label=\"European Languages\">\r\n\t\t\t\t\t\t\t<option value=\"ita\">Italian<\/option>\r\n\t\t\t\t\t\t\t<option value=\"por\">Portuguese<\/option>\r\n\t\t\t\t\t\t\t<option value=\"pol\">Polish<\/option>\r\n\t\t\t\t\t\t\t<option value=\"ukr\">Ukrainian<\/option>\r\n\t\t\t\t\t\t\t<option value=\"ron\">Romanian<\/option>\r\n\t\t\t\t\t\t\t<option value=\"nld\">Dutch<\/option>\r\n\t\t\t\t\t\t\t<option value=\"tur\">Turkish<\/option>\r\n\t\t\t\t\t\t\t<option value=\"nor\">Norwegian<\/option>\r\n\t\t\t\t\t\t\t<option value=\"swe\">Swedish<\/option>\r\n\t\t\t\t\t\t\t<option value=\"dan\">Danish<\/option>\r\n\t\t\t\t\t\t\t<option value=\"fin\">Finnish<\/option>\r\n\t\t\t\t\t\t\t<option value=\"hun\">Hungarian<\/option>\r\n\t\t\t\t\t\t\t<option value=\"ces\">Czech<\/option>\r\n\t\t\t\t\t\t\t<option value=\"slk\">Slovak<\/option>\r\n\t\t\t\t\t\t\t<option value=\"slv\">Slovenian<\/option>\r\n\t\t\t\t\t\t\t<option value=\"hrv\">Croatian<\/option>\r\n\t\t\t\t\t\t\t<option value=\"bos\">Bosnian<\/option>\r\n\t\t\t\t\t\t\t<option value=\"srp\">Serbian<\/option>\r\n\t\t\t\t\t\t\t<option value=\"srp_latn\">Serbian (Latin)<\/option>\r\n\t\t\t\t\t\t\t<option value=\"bul\">Bulgarian<\/option>\r\n\t\t\t\t\t\t\t<option value=\"mkd\">Macedonian<\/option>\r\n\t\t\t\t\t\t\t<option value=\"bel\">Belarusian<\/option>\r\n\t\t\t\t\t\t\t<option value=\"lav\">Latvian<\/option>\r\n\t\t\t\t\t\t\t<option value=\"lit\">Lithuanian<\/option>\r\n\t\t\t\t\t\t\t<option value=\"est\">Estonian<\/option>\r\n\t\t\t\t\t\t\t<option value=\"isl\">Icelandic<\/option>\r\n\t\t\t\t\t\t\t<option value=\"gle\">Irish<\/option>\r\n\t\t\t\t\t\t\t<option value=\"cym\">Welsh<\/option>\r\n\t\t\t\t\t\t\t<option value=\"cat\">Catalan<\/option>\r\n\t\t\t\t\t\t\t<option value=\"eus\">Basque<\/option>\r\n\t\t\t\t\t\t\t<option value=\"glg\">Galician<\/option>\r\n\t\t\t\t\t\t\t<option value=\"mlt\">Maltese<\/option>\r\n\t\t\t\t\t\t\t<option value=\"ell\">Greek (Modern)<\/option>\r\n\t\t\t\t\t\t\t<option value=\"grc\">Greek (Ancient)<\/option>\r\n\t\t\t\t\t\t<\/optgroup>\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\t<optgroup label=\"Asian Languages\">\r\n\t\t\t\t\t\t\t<option value=\"kor\">Korean<\/option>\r\n\t\t\t\t\t\t\t<option value=\"tha\">Thai<\/option>\r\n\t\t\t\t\t\t\t<option value=\"vie\">Vietnamese<\/option>\r\n\t\t\t\t\t\t\t<option value=\"ind\">Indonesian<\/option>\r\n\t\t\t\t\t\t\t<option value=\"msa\">Malay<\/option>\r\n\t\t\t\t\t\t\t<option value=\"tgl\">Tagalog<\/option>\r\n\t\t\t\t\t\t\t<option value=\"jav\">Javanese<\/option>\r\n\t\t\t\t\t\t\t<option value=\"khm\">Khmer (Cambodian)<\/option>\r\n\t\t\t\t\t\t\t<option value=\"lao\">Lao<\/option>\r\n\t\t\t\t\t\t\t<option value=\"mya\">Burmese<\/option>\r\n\t\t\t\t\t\t\t<option value=\"bod\">Tibetan<\/option>\r\n\t\t\t\t\t\t<\/optgroup>\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\t<optgroup label=\"South Asian Languages\">\r\n\t\t\t\t\t\t\t<option value=\"hin\">Hindi<\/option>\r\n\t\t\t\t\t\t\t<option value=\"ben\">Bengali<\/option>\r\n\t\t\t\t\t\t\t<option value=\"pan\">Punjabi<\/option>\r\n\t\t\t\t\t\t\t<option value=\"mar\">Marathi<\/option>\r\n\t\t\t\t\t\t\t<option value=\"tel\">Telugu<\/option>\r\n\t\t\t\t\t\t\t<option value=\"tam\">Tamil<\/option>\r\n\t\t\t\t\t\t\t<option value=\"guj\">Gujarati<\/option>\r\n\t\t\t\t\t\t\t<option value=\"mal\">Malayalam<\/option>\r\n\t\t\t\t\t\t\t<option value=\"kan\">Kannada<\/option>\r\n\t\t\t\t\t\t\t<option value=\"ori\">Oriya<\/option>\r\n\t\t\t\t\t\t\t<option value=\"asm\">Assamese<\/option>\r\n\t\t\t\t\t\t\t<option value=\"urd\">Urdu<\/option>\r\n\t\t\t\t\t\t\t<option value=\"sin\">Sinhala<\/option>\r\n\t\t\t\t\t\t\t<option value=\"nep\">Nepali<\/option>\r\n\t\t\t\t\t\t\t<option value=\"san\">Sanskrit<\/option>\r\n\t\t\t\t\t\t<\/optgroup>\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\t<optgroup label=\"Middle Eastern & African\">\r\n\t\t\t\t\t\t\t<option value=\"ara\">Arabic<\/option>\r\n\t\t\t\t\t\t\t<option value=\"heb\">Hebrew<\/option>\r\n\t\t\t\t\t\t\t<option value=\"fas\">Persian (Farsi)<\/option>\r\n\t\t\t\t\t\t\t<option value=\"urd\">Urdu<\/option>\r\n\t\t\t\t\t\t\t<option value=\"pus\">Pashto<\/option>\r\n\t\t\t\t\t\t\t<option value=\"kur\">Kurdish<\/option>\r\n\t\t\t\t\t\t\t<option value=\"syr\">Syriac<\/option>\r\n\t\t\t\t\t\t\t<option value=\"amh\">Amharic<\/option>\r\n\t\t\t\t\t\t\t<option value=\"tir\">Tigrinya<\/option>\r\n\t\t\t\t\t\t\t<option value=\"swa\">Swahili<\/option>\r\n\t\t\t\t\t\t\t<option value=\"afr\">Afrikaans<\/option>\r\n\t\t\t\t\t\t<\/optgroup>\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\t<optgroup label=\"Central Asian & Others\">\r\n\t\t\t\t\t\t\t<option value=\"aze\">Azerbaijani<\/option>\r\n\t\t\t\t\t\t\t<option value=\"aze_cyrl\">Azerbaijani (Cyrillic)<\/option>\r\n\t\t\t\t\t\t\t<option value=\"kaz\">Kazakh<\/option>\r\n\t\t\t\t\t\t\t<option value=\"kir\">Kirghiz<\/option>\r\n\t\t\t\t\t\t\t<option value=\"uzb\">Uzbek<\/option>\r\n\t\t\t\t\t\t\t<option value=\"uzb_cyrl\">Uzbek (Cyrillic)<\/option>\r\n\t\t\t\t\t\t\t<option value=\"tgk\">Tajik<\/option>\r\n\t\t\t\t\t\t\t<option value=\"uig\">Uighur<\/option>\r\n\t\t\t\t\t\t\t<option value=\"kat\">Georgian<\/option>\r\n\t\t\t\t\t\t<\/optgroup>\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\t<optgroup label=\"Specialized & Historical\">\r\n\t\t\t\t\t\t\t<option value=\"lat\">Latin<\/option>\r\n\t\t\t\t\t\t\t<option value=\"frk\">German Fraktur<\/option>\r\n\t\t\t\t\t\t\t<option value=\"chr\">Cherokee<\/option>\r\n\t\t\t\t\t\t\t<option value=\"iku\">Inuktitut<\/option>\r\n\t\t\t\t\t\t\t<option value=\"hat\">Haitian Creole<\/option>\r\n\t\t\t\t\t\t\t<option value=\"epo\">Esperanto<\/option>\r\n\t\t\t\t\t\t\t<option value=\"dzo\">Dzongkha<\/option>\r\n\t\t\t\t\t\t\t<option value=\"ceb\">Cebuano<\/option>\r\n\t\t\t\t\t\t\t<option value=\"sqi\">Albanian<\/option>\r\n\t\t\t\t\t\t\t<option value=\"yid\">Yiddish<\/option>\r\n\t\t\t\t\t\t<\/optgroup>\r\n\t\t\t\t\t<\/select>\r\n\t\t\t\t<\/div>\r\n\t\t\t<\/div>\r\n\r\n            <div class=\"file-upload\" id=\"fileUpload\">\r\n                <input type=\"file\" id=\"fileInput\" accept=\".pdf,.png,.jpg,.jpeg,.gif,.bmp,.tiff\" multiple>\r\n                <label for=\"fileInput\" style=\"cursor: pointer; display: block; width: 100%; height: 100%;\">\r\n                    Drag & drop a document here or click to select\r\n                <\/label>\r\n                <div class=\"accepted-formats\">\r\n                    Supported formats: PDF, PNG, JPEG, JPG, GIF, BMP, TIFF\r\n                <\/div>\r\n                <div class=\"file-info\" id=\"fileInfo\"><\/div>\r\n            <\/div>\r\n\r\n            <div class=\"button-group\">\r\n                <button id=\"convertBtn\" disabled>\r\n                    <span>Start OCR<\/span>\r\n                <\/button>\r\n            <\/div>\r\n\r\n            <div class=\"status\" id=\"statusMessage\"><\/div>\r\n        <\/div>\r\n\r\n        <div class=\"card output-section\" id=\"outputSection\">\r\n            <div class=\"output-header\">\r\n                <h3>OCR Processing Results<\/h3>\r\n                <div class=\"output-controls\">\r\n                    <button class=\"copy-btn\" id=\"copyBtn\">Copy<\/button>\r\n                    <button class=\"download-btn\" id=\"downloadBtn\">Download<\/button>\r\n                <\/div>\r\n            <\/div>\r\n            \r\n            <div class=\"search-container\">\r\n                <input type=\"text\" id=\"searchInput\" placeholder=\"Search in processed text...\">\r\n                <div class=\"search-info\" id=\"searchInfo\"><\/div>\r\n            <\/div>\r\n            \r\n            <textarea id=\"outputText\" readonly placeholder=\"OCR processed text will appear here...\"><\/textarea>\r\n            \r\n            <div class=\"button-group\" id=\"enhanceButtonGroup\" style=\"display: none; margin-top: 20px;\">\r\n                <button id=\"enhanceBtn\" class=\"enhance-btn\" disabled>\r\n                    <span>Enhance with AI<\/span>\r\n                <\/button>\r\n            <\/div>\r\n\r\n            <div class=\"status\" id=\"enhanceStatusMessage\"><\/div>\r\n        <\/div>\r\n\r\n        <div class=\"card output-section\" id=\"aiCleanedSection\">\r\n            <div class=\"output-header\">\r\n                <h3>Enhanced Document Content<\/h3>\r\n                <div class=\"output-controls\">\r\n                    <button class=\"copy-btn\" id=\"aiCopyBtn\">Copy<\/button>\r\n                    <button class=\"download-btn\" id=\"aiDownloadBtn\">Download<\/button>\r\n                <\/div>\r\n            <\/div>\r\n            \r\n            <div class=\"search-container\">\r\n                <input type=\"text\" id=\"aiSearchInput\" placeholder=\"Search in enhanced content...\">\r\n                <div class=\"search-info\" id=\"aiSearchInfo\"><\/div>\r\n            <\/div>\r\n            \r\n            <textarea id=\"aiOutputText\" readonly placeholder=\"Enhanced document content will appear here...\"><\/textarea>\r\n        <\/div>\r\n\r\n        <div class=\"acknowledgement\">\r\n            <p>This tool is powered by <a href=\"https:\/\/github.com\/tesseract-ocr\/tesseract\" target=\"_blank\" rel=\"noopener noreferrer\">Tesseract OCR<\/a>.<\/p>\r\n        <\/div>\r\n    <\/div>\r\n\r\n    <script type=\"module\">\r\n        \/\/ Configure PDF.js worker\r\n        pdfjsLib.GlobalWorkerOptions.workerSrc = \"https:\/\/cdn.jsdelivr.net\/npm\/pdfjs-dist@3.6.172\/build\/pdf.worker.js\";\r\n\r\n        \/\/ =====================================\r\n        \/\/ BACKEND AUTHORISATION SYSTEM\r\n        \/\/ =====================================\r\n        function modInverse(k1, mod = 256) {\r\n            let t = 0, newT = 1, r = mod, newR = k1;\r\n            while (newR !== 0) {\r\n                const quotient = Math.floor(r \/ newR);\r\n                [t, newT] = [newT, t - quotient * newT];\r\n                [r, newR] = [newR, r - quotient * newR];\r\n            }\r\n            if (r > 1) throw new Error('k1 is not invertible');\r\n            if (t < 0) t += mod;\r\n            return t;\r\n        }\r\n\r\n        function decryptServerPayload(payload) {\r\n            if (payload.length < 12) throw new Error('Invalid payload: too short');\r\n            const suffix = payload.slice(-12), b64 = payload.slice(0, -12);\r\n            const numericKey = BigInt(suffix.slice(0, 10));\r\n            const k1 = Number((numericKey % 127n) * 2n + 1n), k2 = Number(numericKey % 256n);\r\n            const invK1 = modInverse(k1, 256);\r\n            const binaryString = atob(b64);\r\n            const bytes = new Uint8Array(binaryString.length);\r\n            for (let i = 0; i < binaryString.length; i++) bytes[i] = binaryString.charCodeAt(i);\r\n            const decryptedBytes = new Uint8Array(bytes.length);\r\n            for (let i = 0; i < bytes.length; i++) {\r\n                const temp = (bytes[i] - k2 + 256) % 256;\r\n                decryptedBytes[i] = (invK1 * temp) % 256;\r\n            }\r\n            return new TextDecoder('utf-8').decode(decryptedBytes);\r\n        }\r\n\r\n        async function fetchDecryptedKey(service) {\r\n            const sanitizedBase = API_BASE_URL.endsWith('\/') ? API_BASE_URL.slice(0, -1) : API_BASE_URL;\r\n            const url = `${sanitizedBase}\/get_encrypted_key?service=${service}`;\r\n            const response = await fetch(url, { method: 'GET', headers: { 'Accept': 'application\/json' } });\r\n            if (!response.ok) throw new Error(`Failed to fetch ${service} key: ${response.status}`);\r\n            const data = await response.json();\r\n            if (!data.encrypted) throw new Error(`No encrypted key returned for ${service}`);\r\n            return decryptServerPayload(data.encrypted);\r\n        }\r\n\r\n        async function initializeApiKeys() {\r\n            return { cozeKey: await fetchDecryptedKey('coze') };\r\n        }\r\n\r\n        function clearApiKeys() {\r\n            if (typeof COZE_API_KEY_RUNTIME !== 'undefined') COZE_API_KEY_RUNTIME = null;\r\n        }\r\n\r\n        async function retryWithKeyRefresh(apiCallFn, maxRetries = 3) {\r\n            let lastError;\r\n            for (let attempt = 1; attempt <= maxRetries; attempt++) {\r\n                try {\r\n                    return await apiCallFn();\r\n                } catch (error) {\r\n                    lastError = error;\r\n                    const is401or403 = error.message.includes('401') || error.message.includes('403');\r\n                    if (is401or403 && attempt < maxRetries) {\r\n                        COZE_API_KEY_RUNTIME = await fetchDecryptedKey('coze');\r\n                    } else {\r\n                        throw lastError;\r\n                    }\r\n                }\r\n            }\r\n            throw lastError;\r\n        }\r\n\r\n        \/\/ =====================================\r\n        \/\/ API Configuration\r\n        \/\/ =====================================\r\n        let COZE_API_KEY_RUNTIME = null;\r\n        Object.defineProperty(window, 'COZE_API_KEY', {\r\n            get() { return COZE_API_KEY_RUNTIME; },\r\n            configurable: false\r\n        });\r\n\r\n        const API_BASE_URL = (() => {\r\n            try {\r\n                const { protocol, hostname } = window.location;\r\n                if (protocol === 'https:' && (hostname === 'oetools.net' || hostname.endsWith('.oetools.net'))) {\r\n                    return '\/api';\r\n                }\r\n            } catch (error) {}\r\n            return 'https:\/\/oetools.net\/api';\r\n        })();\r\n\r\n        const COZE_API_URL = 'https:\/\/api.coze.com\/open_api\/v2\/chat';\r\n        const COZE_BOT_ID = '7550199842439036936';\r\n        const USER_ID = 'coze_user_' + Date.now();\r\n\r\n        \/\/ Global variables\r\n        let selectedFiles = [];\r\n        let extractedText = '';\r\n        let aiCleanedText = '';\r\n        let processingStartTime = 0;\r\n\r\n        \/\/ DOM elements\r\n        const fileInput = document.getElementById('fileInput');\r\n        const fileUpload = document.getElementById('fileUpload');\r\n        const fileInfo = document.getElementById('fileInfo');\r\n        const languageSelect = document.getElementById('languageSelect');\r\n        const convertBtn = document.getElementById('convertBtn');\r\n        const statusMessage = document.getElementById('statusMessage');\r\n        const outputSection = document.getElementById('outputSection');\r\n        const outputText = document.getElementById('outputText');\r\n        const copyBtn = document.getElementById('copyBtn');\r\n        const downloadBtn = document.getElementById('downloadBtn');\r\n        const enhanceBtn = document.getElementById('enhanceBtn');\r\n        const enhanceButtonGroup = document.getElementById('enhanceButtonGroup');\r\n        const enhanceStatusMessage = document.getElementById('enhanceStatusMessage');\r\n        const searchInput = document.getElementById('searchInput');\r\n        const searchInfo = document.getElementById('searchInfo');\r\n        \r\n        \/\/ AI Cleaned section elements\r\n        const aiCleanedSection = document.getElementById('aiCleanedSection');\r\n        const aiOutputText = document.getElementById('aiOutputText');\r\n        const aiCopyBtn = document.getElementById('aiCopyBtn');\r\n        const aiDownloadBtn = document.getElementById('aiDownloadBtn');\r\n        const aiSearchInput = document.getElementById('aiSearchInput');\r\n        const aiSearchInfo = document.getElementById('aiSearchInfo');\r\n\r\n        \/\/ File upload handling\r\n        let isFileDialogOpen = false;\r\n        \r\n        fileInput.addEventListener('change', handleFileSelection);\r\n        fileInput.addEventListener('click', (e) => {\r\n            \/\/ Prevent the click event from bubbling up to fileUpload\r\n            e.stopPropagation();\r\n        });\r\n        \r\n        \/\/ Get the label element\r\n        const fileLabel = fileUpload.querySelector('label[for=\"fileInput\"]');\r\n        if (fileLabel) {\r\n            fileLabel.addEventListener('click', (e) => {\r\n                \/\/ Let the label's natural behavior trigger the fileInput\r\n                \/\/ but prevent it from bubbling to fileUpload\r\n                e.stopPropagation();\r\n            });\r\n        }\r\n        \r\n        fileUpload.addEventListener('click', (e) => {\r\n            \/\/ Prevent double-triggering: if click is from fileInput, label, or dialog is already open\r\n            if (e.target === fileInput || e.target === fileLabel || e.target.closest('label[for=\"fileInput\"]') || isFileDialogOpen) {\r\n                return;\r\n            }\r\n            isFileDialogOpen = true;\r\n            fileInput.click();\r\n            \/\/ Reset flag after a delay to allow file selection to complete\r\n            setTimeout(() => {\r\n                isFileDialogOpen = false;\r\n            }, 300);\r\n        });\r\n        fileUpload.addEventListener('dragover', handleDragOver);\r\n        fileUpload.addEventListener('dragleave', handleDragLeave);\r\n        fileUpload.addEventListener('drop', handleDrop);\r\n\r\n        \/\/ Button handlers\r\n        convertBtn.addEventListener('click', startOCR);\r\n        copyBtn.addEventListener('click', copyToClipboard);\r\n        downloadBtn.addEventListener('click', downloadText);\r\n        enhanceBtn.addEventListener('click', sendToAICleaning);\r\n        searchInput.addEventListener('input', handleSearch);\r\n        \r\n        \/\/ AI Cleaned section handlers\r\n        aiCopyBtn.addEventListener('click', copyAICleanedText);\r\n        aiDownloadBtn.addEventListener('click', downloadAICleanedText);\r\n        aiSearchInput.addEventListener('input', handleAISearch);\r\n\r\n        function handleFileSelection(event) {\r\n            const files = Array.from(event.target.files);\r\n            updateSelectedFiles(files);\r\n        }\r\n\r\n        function handleDragOver(event) {\r\n            event.preventDefault();\r\n            fileUpload.classList.add('dragover');\r\n        }\r\n\r\n        function handleDragLeave(event) {\r\n            event.preventDefault();\r\n            fileUpload.classList.remove('dragover');\r\n        }\r\n\r\n        function handleDrop(event) {\r\n            event.preventDefault();\r\n            fileUpload.classList.remove('dragover');\r\n            const files = Array.from(event.dataTransfer.files);\r\n            updateSelectedFiles(files);\r\n        }\r\n\r\n        function updateSelectedFiles(files) {\r\n            selectedFiles = files.filter(file => {\r\n                const validTypes = ['application\/pdf', 'image\/'];\r\n                const isValid = validTypes.some(type => {\r\n                    if (type === 'image\/') {\r\n                        return file.type.startsWith('image\/');\r\n                    }\r\n                    return file.type === type;\r\n                });\r\n                return isValid;\r\n            });\r\n\r\n            if (selectedFiles.length > 0) {\r\n                const fileNames = selectedFiles.map(f => f.name).join(', ');\r\n                fileInfo.textContent = `Selected: ${fileNames}`;\r\n                convertBtn.disabled = false;\r\n            } else {\r\n                fileInfo.textContent = 'No valid files selected';\r\n                convertBtn.disabled = true;\r\n            }\r\n        }\r\n\r\n        async function startOCR() {\r\n            if (selectedFiles.length === 0) return;\r\n\r\n            processingStartTime = Date.now();\r\n            convertBtn.disabled = true;\r\n            outputSection.classList.remove('show');\r\n            extractedText = '';\r\n            \r\n            showStatus('info', 'Starting OCR process...');\r\n\r\n            try {\r\n                for (let i = 0; i < selectedFiles.length; i++) {\r\n                    const file = selectedFiles[i];\r\n                    showStatus('info', `<div class=\"spinner\"><\/div>Processing file ${i + 1} of ${selectedFiles.length}: ${file.name}`);\r\n                    \r\n                    const text = await processFile(file);\r\n                    if (text.trim()) {\r\n                        extractedText += `\\n--- ${file.name} ---\\n${text}\\n`;\r\n                    }\r\n                }\r\n\r\n                const processingTime = ((Date.now() - processingStartTime) \/ 1000).toFixed(1);\r\n                showStatus('success', `OCR completed successfully in ${processingTime} seconds`);\r\n                \r\n                displayResults();\r\n            } catch (error) {\r\n                const processingTime = ((Date.now() - processingStartTime) \/ 1000).toFixed(1);\r\n                showStatus('error', `Error during OCR processing (${processingTime}s): ${error.message}`);\r\n            } finally {\r\n                convertBtn.disabled = false;\r\n            }\r\n        }\r\n\r\n        async function processFile(file) {\r\n            const language = languageSelect.value;\r\n\r\n            if (file.type === 'application\/pdf') {\r\n                return await processPDF(file, language);\r\n            } else if (file.type.startsWith('image\/')) {\r\n                return await processImage(file, language);\r\n            } else {\r\n                throw new Error(`Unsupported file type: ${file.type}`);\r\n            }\r\n        }\r\n\r\n        async function processPDF(file, language) {\r\n            const arrayBuffer = await file.arrayBuffer();\r\n            const pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise;\r\n            \r\n            let fullText = '';\r\n            \r\n            for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) {\r\n                showStatus('info', `<div class=\"spinner\"><\/div>Processing PDF page ${pageNum} of ${pdf.numPages}`);\r\n                \r\n                const page = await pdf.getPage(pageNum);\r\n                const viewport = page.getViewport({ scale: 2.0 });\r\n                \r\n                const canvas = document.createElement('canvas');\r\n                const context = canvas.getContext('2d');\r\n                canvas.height = viewport.height;\r\n                canvas.width = viewport.width;\r\n                \r\n                await page.render({\r\n                    canvasContext: context,\r\n                    viewport: viewport\r\n                }).promise;\r\n                \r\n                const imageData = canvas.toDataURL('image\/png');\r\n                const pageText = await performOCR(imageData, language);\r\n                \r\n                if (pageText.trim()) {\r\n                    fullText += `\\n--- Page ${pageNum} ---\\n${pageText}\\n`;\r\n                }\r\n            }\r\n            \r\n            return fullText;\r\n        }\r\n\r\n        async function processImage(file, language) {\r\n            const imageUrl = URL.createObjectURL(file);\r\n            try {\r\n                const text = await performOCR(imageUrl, language);\r\n                return text;\r\n            } finally {\r\n                URL.revokeObjectURL(imageUrl);\r\n            }\r\n        }\r\n\r\n        async function performOCR(imageSource, language) {\r\n            const worker = await Tesseract.createWorker();\r\n            await worker.loadLanguage(language);\r\n            await worker.initialize(language);\r\n            \r\n            const { data: { text } } = await worker.recognize(imageSource);\r\n            await worker.terminate();\r\n            \r\n            return text;\r\n        }\r\n\r\n        function displayResults() {\r\n            outputText.value = extractedText.trim();\r\n            outputSection.classList.add('show');\r\n            enhanceButtonGroup.style.display = 'block';\r\n            enhanceBtn.disabled = false;\r\n            updateSearchInfo();\r\n        }\r\n\r\n        function showStatus(type, message) {\r\n            statusMessage.className = `status ${type}`;\r\n            statusMessage.innerHTML = message;\r\n        }\r\n\r\n        function showEnhanceStatus(type, message) {\r\n            enhanceStatusMessage.className = `status ${type}`;\r\n            enhanceStatusMessage.innerHTML = message;\r\n        }\r\n\r\n        function copyToClipboard() {\r\n            outputText.select();\r\n            document.execCommand('copy');\r\n            showStatus('success', 'OCR processed text copied to clipboard!');\r\n            setTimeout(() => {\r\n                statusMessage.classList.remove('success');\r\n            }, 2000);\r\n        }\r\n\r\n        function downloadText() {\r\n            const blob = new Blob([extractedText], { type: 'text\/plain' });\r\n            const url = URL.createObjectURL(blob);\r\n            const a = document.createElement('a');\r\n            a.href = url;\r\n            a.download = 'ocr_processed_text.txt';\r\n            document.body.appendChild(a);\r\n            a.click();\r\n            document.body.removeChild(a);\r\n            URL.revokeObjectURL(url);\r\n            \r\n            showStatus('success', 'OCR processed text file downloaded!');\r\n            setTimeout(() => {\r\n                statusMessage.classList.remove('success');\r\n            }, 2000);\r\n        }\r\n\r\n        function handleSearch() {\r\n            const searchTerm = searchInput.value.toLowerCase();\r\n            const text = outputText.value;\r\n            \r\n            if (!searchTerm) {\r\n                updateSearchInfo();\r\n                return;\r\n            }\r\n\r\n            const matches = (text.toLowerCase().match(new RegExp(searchTerm, 'g')) || []).length;\r\n            searchInfo.textContent = `${matches} matches found`;\r\n            \r\n            \/\/ Highlight first match\r\n            if (matches > 0) {\r\n                const index = text.toLowerCase().indexOf(searchTerm);\r\n                outputText.focus();\r\n                outputText.setSelectionRange(index, index + searchTerm.length);\r\n            }\r\n        }\r\n\r\n        function updateSearchInfo() {\r\n            const wordCount = extractedText.trim() ? extractedText.trim().split(\/\\s+\/).length : 0;\r\n            const charCount = extractedText.length;\r\n            searchInfo.textContent = `${wordCount} words, ${charCount} characters`;\r\n        }\r\n\r\n        \/\/ =====================================\r\n        \/\/ AI Cleaning Functions\r\n        \/\/ =====================================\r\n        \r\n        \/**\r\n         * Function 1: Prepend OCR cleaning instruction to user query\r\n         * @param {string} userQuery - The original user input\r\n         * @returns {string} - Query with OCR cleaning instruction prepended\r\n         *\/\r\n        function prepareOCRQuery(userQuery) {\r\n            const ocrInstruction = \"Clean the following OCR results: \";\r\n            return ocrInstruction + userQuery;\r\n        }\r\n        \r\n        \/**\r\n         * Function 2: Parse the assistant's response content\r\n         * @param {Object} apiResponse - The full API response from Coze\r\n         * @returns {string} - Extracted assistant content or error message\r\n         *\/\r\n        function parseAssistantResponse(apiResponse) {\r\n            try {\r\n                \/\/ Check if the response has the expected structure\r\n                if (!apiResponse || !apiResponse.messages || !Array.isArray(apiResponse.messages)) {\r\n                    return \"Error: Invalid response structure - no messages array found\";\r\n                }\r\n                \r\n                \/\/ Find the assistant message with type \"answer\"\r\n                const assistantMessage = apiResponse.messages.find(msg => \r\n                    msg.role === \"assistant\" && msg.type === \"answer\"\r\n                );\r\n                \r\n                if (!assistantMessage) {\r\n                    return \"Error: No assistant answer found in response\";\r\n                }\r\n                \r\n                \/\/ Extract the content\r\n                if (assistantMessage.content) {\r\n                    return assistantMessage.content;\r\n                } else {\r\n                    return \"Error: Assistant message found but content is empty\";\r\n                }\r\n                \r\n            } catch (error) {\r\n                return `Error parsing response: ${error.message}`;\r\n            }\r\n        }\r\n        \r\n        \/\/ Main function to send request to Coze API\r\n        async function sendToAICleaning() {\r\n            const inputText = outputText.value.trim();\r\n            \r\n            if (!inputText) {\r\n                showEnhanceStatus('error', 'No OCR processed text available for enhancement');\r\n                setTimeout(() => showEnhanceStatus('info', 'Complete OCR processing first'), 2000);\r\n                return;\r\n            }\r\n            \r\n            \/\/ Prepare the OCR query using Function 1\r\n            const ocrQuery = prepareOCRQuery(inputText);\r\n            \r\n            \/\/ Update UI state\r\n            showEnhanceStatus('info', 'Enhancing document content with AI...');\r\n            enhanceBtn.disabled = true;\r\n            enhanceBtn.textContent = 'AI Processing';\r\n            aiCleanedSection.classList.remove('show');\r\n            aiCleanedText = '';\r\n            \r\n            try {\r\n                \/\/ Prepare the payload with OCR query\r\n                const payload = {\r\n                    bot_id: COZE_BOT_ID,\r\n                    user: USER_ID,\r\n                    query: ocrQuery,  \/\/ Using the prepared OCR query\r\n                    stream: false\r\n                };\r\n                \r\n                \/\/ Send the request to Coze API\r\n                const performCozeCall = async () => {\r\n                    const response = await fetch(COZE_API_URL, {\r\n                        method: 'POST',\r\n                        headers: {\r\n                            'Content-Type': 'application\/json',\r\n                            'Authorization': `Bearer ${COZE_API_KEY}`,\r\n                            'Accept': '*\/*'\r\n                        },\r\n                        body: JSON.stringify(payload)\r\n                    });\r\n                    \r\n                    if (!response.ok) {\r\n                        const errorText = await response.text();\r\n                        throw new Error(`HTTP ${response.status}: ${errorText}`);\r\n                    }\r\n                    \r\n                    return await response.json();\r\n                };\r\n                \r\n                const data = await retryWithKeyRefresh(performCozeCall);\r\n                \r\n                \/\/ Parse and display the cleaned OCR results using Function 2\r\n                const cleanedText = parseAssistantResponse(data);\r\n                aiCleanedText = cleanedText;\r\n                \r\n                displayAICleanedResults();\r\n                showEnhanceStatus('success', 'Document enhancement completed successfully');\r\n                \r\n            } catch (error) {\r\n                const errorMessage = `Enhancement Error: ${error.message}`;\r\n                showEnhanceStatus('error', errorMessage);\r\n            } finally {\r\n                enhanceBtn.disabled = false;\r\n                enhanceBtn.textContent = 'Enhance with AI';\r\n            }\r\n        }\r\n        \r\n        function displayAICleanedResults() {\r\n            aiOutputText.value = aiCleanedText.trim();\r\n            aiCleanedSection.classList.add('show');\r\n            updateAISearchInfo();\r\n        }\r\n        \r\n        function copyAICleanedText() {\r\n            aiOutputText.select();\r\n            document.execCommand('copy');\r\n            showStatus('success', 'Enhanced document content copied to clipboard!');\r\n            setTimeout(() => {\r\n                statusMessage.classList.remove('success');\r\n            }, 2000);\r\n        }\r\n        \r\n        function downloadAICleanedText() {\r\n            const blob = new Blob([aiCleanedText], { type: 'text\/plain' });\r\n            const url = URL.createObjectURL(blob);\r\n            const a = document.createElement('a');\r\n            a.href = url;\r\n            a.download = 'enhanced_document_content.txt';\r\n            document.body.appendChild(a);\r\n            a.click();\r\n            document.body.removeChild(a);\r\n            URL.revokeObjectURL(url);\r\n            \r\n            showStatus('success', 'Enhanced document content file downloaded!');\r\n            setTimeout(() => {\r\n                statusMessage.classList.remove('success');\r\n            }, 2000);\r\n        }\r\n        \r\n        function handleAISearch() {\r\n            const searchTerm = aiSearchInput.value.toLowerCase();\r\n            const text = aiOutputText.value;\r\n            \r\n            if (!searchTerm) {\r\n                updateAISearchInfo();\r\n                return;\r\n            }\r\n\r\n            const matches = (text.toLowerCase().match(new RegExp(searchTerm, 'g')) || []).length;\r\n            aiSearchInfo.textContent = `${matches} matches found`;\r\n            \r\n            \/\/ Highlight first match\r\n            if (matches > 0) {\r\n                const index = text.toLowerCase().indexOf(searchTerm);\r\n                aiOutputText.focus();\r\n                aiOutputText.setSelectionRange(index, index + searchTerm.length);\r\n            }\r\n        }\r\n        \r\n        function updateAISearchInfo() {\r\n            const text = aiOutputText.value.trim();\r\n            const wordCount = text ? text.split(\/\\s+\/).filter(word => word.length > 0).length : 0;\r\n            const charCount = text.length;\r\n            aiSearchInfo.textContent = `${wordCount} words, ${charCount} characters`;\r\n        }\r\n        \r\n        \/\/ Initialize API keys on page load\r\n        window.addEventListener('DOMContentLoaded', async () => {\r\n            try {\r\n                const { cozeKey } = await initializeApiKeys();\r\n                COZE_API_KEY_RUNTIME = cozeKey;\r\n            } catch (error) {\r\n                alert('Authorisation was not granted by the server. This may be due to a network issue. Please check your connection and try again.');\r\n            }\r\n        });\r\n\r\n        window.addEventListener('beforeunload', clearApiKeys);\r\n    <\/script>\r\n<\/body>\r\n<\/html>\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>Instant OCR Tool Instant OCR Tool Extract text accurately from PDFs and images (PNG, JPG, GIF, BMP, TIFF) with 95 language options. Refine results instantly with one\u2011click AI cleanup for pristine output. File Upload &#038; OCR Settings OCR Language: English Chinese (Simplified) Chinese (Traditional) Spanish French German Japanese Russian Italian Portuguese Polish Ukrainian Romanian Dutch...<\/p>\n","protected":false},"author":748,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"redux-templates_canvas","meta":{"_expiration-date-status":"","_expiration-date":0,"_expiration-date-type":"","_expiration-date-categories":[],"_expiration-date-options":[]},"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v17.3 (Yoast SEO v21.2) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Agent 7 OCR - Hong Kong Metropolitan University<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.hkmu.edu.hk\/oetools\/agent-7-ocr\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Agent 7 OCR\" \/>\n<meta property=\"og:description\" content=\"Instant OCR Tool Instant OCR Tool Extract text accurately from PDFs and images (PNG, JPG, GIF, BMP, TIFF) with 95 language options. Refine results\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.hkmu.edu.hk\/oetools\/agent-7-ocr\/\" \/>\n<meta property=\"og:site_name\" content=\"Hong Kong Metropolitan University\" \/>\n<meta property=\"article:modified_time\" content=\"2025-11-03T09:08:18+00:00\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"1 minute\" \/>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Agent 7 OCR - Hong Kong Metropolitan University","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.hkmu.edu.hk\/oetools\/agent-7-ocr\/","og_locale":"en_US","og_type":"article","og_title":"Agent 7 OCR","og_description":"Instant OCR Tool Instant OCR Tool Extract text accurately from PDFs and images (PNG, JPG, GIF, BMP, TIFF) with 95 language options. Refine results","og_url":"https:\/\/www.hkmu.edu.hk\/oetools\/agent-7-ocr\/","og_site_name":"Hong Kong Metropolitan University","article_modified_time":"2025-11-03T09:08:18+00:00","twitter_card":"summary_large_image","twitter_misc":{"Est. reading time":"1 minute"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.hkmu.edu.hk\/oetools\/agent-7-ocr\/","url":"https:\/\/www.hkmu.edu.hk\/oetools\/agent-7-ocr\/","name":"Agent 7 OCR - Hong Kong Metropolitan University","isPartOf":{"@id":"https:\/\/www.hkmu.edu.hk\/oetools\/#website"},"datePublished":"2025-09-15T09:06:15+00:00","dateModified":"2025-11-03T09:08:18+00:00","breadcrumb":{"@id":"https:\/\/www.hkmu.edu.hk\/oetools\/agent-7-ocr\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.hkmu.edu.hk\/oetools\/agent-7-ocr\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.hkmu.edu.hk\/oetools\/agent-7-ocr\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Open Educational Tools","item":"\/oetools\/"},{"@type":"ListItem","position":2,"name":"Agent 7 OCR"}]},{"@type":"WebSite","@id":"https:\/\/www.hkmu.edu.hk\/oetools\/#website","url":"https:\/\/www.hkmu.edu.hk\/oetools\/","name":"Hong Kong Metropolitan University","description":"Open Educational Tools - Hong Kong Metropolitan University","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.hkmu.edu.hk\/oetools\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"}]}},"_links":{"self":[{"href":"https:\/\/www.hkmu.edu.hk\/oetools\/wp-json\/wp\/v2\/pages\/30416"}],"collection":[{"href":"https:\/\/www.hkmu.edu.hk\/oetools\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.hkmu.edu.hk\/oetools\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.hkmu.edu.hk\/oetools\/wp-json\/wp\/v2\/users\/748"}],"replies":[{"embeddable":true,"href":"https:\/\/www.hkmu.edu.hk\/oetools\/wp-json\/wp\/v2\/comments?post=30416"}],"version-history":[{"count":17,"href":"https:\/\/www.hkmu.edu.hk\/oetools\/wp-json\/wp\/v2\/pages\/30416\/revisions"}],"predecessor-version":[{"id":30923,"href":"https:\/\/www.hkmu.edu.hk\/oetools\/wp-json\/wp\/v2\/pages\/30416\/revisions\/30923"}],"wp:attachment":[{"href":"https:\/\/www.hkmu.edu.hk\/oetools\/wp-json\/wp\/v2\/media?parent=30416"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}