From b739a6d286a3c1774708a0389f4eb1a7099c3593 Mon Sep 17 00:00:00 2001 From: EthanHealy01 Date: Tue, 29 Jul 2025 15:49:51 +0100 Subject: [PATCH] Added OCR, working on the translation files for displaying different tesseract languages. going to commit this before I do that --- .../src/main/resources/application.properties | 2 + .../public/locales/ar-AR/translation.json | 133 +++++++++- .../public/locales/de-DE/translation.json | 109 ++++++++ .../public/locales/en-US/translation.json | 109 ++++++++ .../src/components/tools/ocr/OCRSettings.tsx | 161 ++++++++++++ .../src/hooks/tools/ocr/useOCROperation.ts | 245 ++++++++++++++++++ .../src/hooks/tools/ocr/useOCRParameters.ts | 43 +++ frontend/src/hooks/useEndpointConfig.ts | 58 ++++- frontend/src/hooks/useToolManagement.tsx | 35 ++- frontend/src/tools/OCR.tsx | 175 +++++++++++++ frontend/src/types/fileContext.ts | 18 +- 11 files changed, 1063 insertions(+), 25 deletions(-) create mode 100644 frontend/src/components/tools/ocr/OCRSettings.tsx create mode 100644 frontend/src/hooks/tools/ocr/useOCROperation.ts create mode 100644 frontend/src/hooks/tools/ocr/useOCRParameters.ts create mode 100644 frontend/src/tools/OCR.tsx diff --git a/app/core/src/main/resources/application.properties b/app/core/src/main/resources/application.properties index ea30bf78e..f4b6b0a89 100644 --- a/app/core/src/main/resources/application.properties +++ b/app/core/src/main/resources/application.properties @@ -7,6 +7,8 @@ logging.level.org.eclipse.jetty=WARN #logging.level.org.opensaml=DEBUG #logging.level.stirling.software.SPDF.config.security: DEBUG logging.level.com.zaxxer.hikari=WARN +# Enable OCR controller logging for debugging +logging.level.stirling.software.SPDF.controller.api.misc.OCRController=INFO spring.jpa.open-in-view=false server.forward-headers-strategy=NATIVE server.error.path=/error diff --git a/frontend/public/locales/ar-AR/translation.json b/frontend/public/locales/ar-AR/translation.json index e6b5b13cb..5c4da06d1 100644 --- a/frontend/public/locales/ar-AR/translation.json +++ b/frontend/public/locales/ar-AR/translation.json @@ -1399,7 +1399,7 @@ "success": "File decrypted successfully." }, "multiTool-advert": { - "message": "هذه الميزة متوفرة في صفحة الأدوات المتعددة لدينا. اطلع عليها للحصول على واجهة مستخدم محسّنة لكل صفحة وميزات إضافية!" + "message": "هذه الميزة متوفرة في صفحة الأدوات المتعددة لدينا. اطلع عليها للحصول على واجهة مستخدم محسّنة لكل صفحة وميزات إضافية!" }, "pageRemover": { "title": "مزيل الصفحة", @@ -1521,6 +1521,137 @@ }, "note": "Release notes are only available in English" }, + "lang": { + "afr": "Afrikaans", + "amh": "Amharic", + "ara": "Arabic", + "asm": "Assamese", + "aze": "Azerbaijani", + "aze_cyrl": "Azerbaijani (Cyrillic)", + "bel": "Belarusian", + "ben": "Bengali", + "bod": "Tibetan", + "bos": "Bosnian", + "bre": "Breton", + "bul": "Bulgarian", + "cat": "Catalan", + "ceb": "Cebuano", + "ces": "Czech", + "chi_sim": "Chinese (Simplified)", + "chi_sim_vert": "Chinese (Simplified, Vertical)", + "chi_tra": "Chinese (Traditional)", + "chi_tra_vert": "Chinese (Traditional, Vertical)", + "chr": "Cherokee", + "cos": "Corsican", + "cym": "Welsh", + "dan": "Danish", + "dan_frak": "Danish (Fraktur)", + "deu": "German", + "deu_frak": "German (Fraktur)", + "div": "Divehi", + "dzo": "Dzongkha", + "ell": "Greek", + "eng": "English", + "enm": "English, Middle (1100-1500)", + "epo": "Esperanto", + "equ": "Math / equation detection module", + "est": "Estonian", + "eus": "Basque", + "fao": "Faroese", + "fas": "Persian", + "fil": "Filipino", + "fin": "Finnish", + "fra": "French", + "frk": "Frankish", + "frm": "French, Middle (ca.1400-1600)", + "fry": "Western Frisian", + "gla": "Scottish Gaelic", + "gle": "Irish", + "glg": "Galician", + "grc": "Ancient Greek", + "guj": "Gujarati", + "hat": "Haitian, Haitian Creole", + "heb": "Hebrew", + "hin": "Hindi", + "hrv": "Croatian", + "hun": "Hungarian", + "hye": "Armenian", + "iku": "Inuktitut", + "ind": "Indonesian", + "isl": "Icelandic", + "ita": "Italian", + "ita_old": "Italian (Old)", + "jav": "Javanese", + "jpn": "Japanese", + "jpn_vert": "Japanese (Vertical)", + "kan": "Kannada", + "kat": "Georgian", + "kat_old": "Georgian (Old)", + "kaz": "Kazakh", + "khm": "Central Khmer", + "kir": "Kirghiz, Kyrgyz", + "kmr": "Northern Kurdish", + "kor": "Korean", + "kor_vert": "Korean (Vertical)", + "lao": "Lao", + "lat": "Latin", + "lav": "Latvian", + "lit": "Lithuanian", + "ltz": "Luxembourgish", + "mal": "Malayalam", + "mar": "Marathi", + "mkd": "Macedonian", + "mlt": "Maltese", + "mon": "Mongolian", + "mri": "Maori", + "msa": "Malay", + "mya": "Burmese", + "nep": "Nepali", + "nld": "Dutch; Flemish", + "nor": "Norwegian", + "oci": "Occitan (post 1500)", + "ori": "Oriya", + "osd": "Orientation and script detection module", + "pan": "Panjabi, Punjabi", + "pol": "Polish", + "por": "Portuguese", + "pus": "Pushto, Pashto", + "que": "Quechua", + "ron": "Romanian, Moldavian, Moldovan", + "rus": "Russian", + "san": "Sanskrit", + "sin": "Sinhala, Sinhalese", + "slk": "Slovak", + "slk_frak": "Slovak (Fraktur)", + "slv": "Slovenian", + "snd": "Sindhi", + "spa": "Spanish", + "spa_old": "Spanish (Old)", + "sqi": "Albanian", + "srp": "Serbian", + "srp_latn": "Serbian (Latin)", + "sun": "Sundanese", + "swa": "Swahili", + "swe": "Swedish", + "syr": "Syriac", + "tam": "Tamil", + "tat": "Tatar", + "tel": "Telugu", + "tgk": "Tajik", + "tgl": "Tagalog", + "tha": "Thai", + "tir": "Tigrinya", + "ton": "Tonga (Tonga Islands)", + "tur": "Turkish", + "uig": "Uighur, Uyghur", + "ukr": "Ukrainian", + "urd": "Urdu", + "uzb": "Uzbek", + "uzb_cyrl": "Uzbek (Cyrillic)", + "vie": "Vietnamese", + "yid": "Yiddish", + "yor": "Yoruba" + }, "cookieBanner": { "popUp": { "title": "How we use Cookies", diff --git a/frontend/public/locales/de-DE/translation.json b/frontend/public/locales/de-DE/translation.json index c5127bd14..476b57bdc 100644 --- a/frontend/public/locales/de-DE/translation.json +++ b/frontend/public/locales/de-DE/translation.json @@ -1521,6 +1521,115 @@ }, "note": "Versionshinweise sind nur auf Englisch verfügbar" }, + "lang": { + "eng": "Englisch", + "fra": "Französisch", + "deu": "Deutsch", + "spa": "Spanisch", + "ita": "Italienisch", + "por": "Portugiesisch", + "rus": "Russisch", + "chi_sim": "Chinesisch (vereinfacht)", + "chi_sim_vert": "Chinesisch (vereinfacht, vertikal)", + "chi_tra": "Chinesisch (traditionell)", + "chi_tra_vert": "Chinesisch (traditionell, vertikal)", + "jpn": "Japanisch", + "jpn_vert": "Japanisch (vertikal)", + "kor": "Koreanisch", + "kor_vert": "Koreanisch (vertikal)", + "ara": "Arabisch", + "hin": "Hindi", + "nld": "Niederländisch", + "ces": "Tschechisch", + "pol": "Polnisch", + "tur": "Türkisch", + "ukr": "Ukrainisch", + "vie": "Vietnamesisch", + "swe": "Schwedisch", + "nor": "Norwegisch", + "fin": "Finnisch", + "dan": "Dänisch", + "ell": "Griechisch", + "heb": "Hebräisch", + "hun": "Ungarisch", + "bul": "Bulgarisch", + "ron": "Rumänisch", + "hrv": "Kroatisch", + "slk": "Slowakisch", + "ind": "Indonesisch", + "tha": "Thailändisch", + "slv": "Slowenisch", + "lav": "Lettisch", + "lit": "Litauisch", + "est": "Estnisch", + "cat": "Katalanisch", + "eus": "Baskisch", + "glg": "Galicisch", + "oci": "Okzitanisch", + "afr": "Afrikaans", + "amh": "Amharisch", + "asm": "Assamesisch", + "aze": "Aserbaidschanisch", + "aze_cyrl": "Aserbaidschanisch (kyrillisch)", + "bel": "Weißrussisch", + "ben": "Bengalisch", + "bod": "Tibetisch", + "bos": "Bosnisch", + "bre": "Bretonisch", + "ceb": "Cebuano", + "chr": "Cherokee", + "cym": "Walisisch", + "dzo": "Dzongkha", + "epo": "Esperanto", + "equ": "Mathematik / Gleichungserkennung", + "fas": "Persisch", + "fil": "Filipino", + "fry": "Westfriesisch", + "gle": "Irisch", + "guj": "Gujarati", + "hat": "Haitianisches Kreolisch", + "iku": "Inuktitut", + "jav": "Javanisch", + "kan": "Kannada", + "kaz": "Kasachisch", + "kaz_cyrl": "Kasachisch (kyrillisch)", + "khm": "Khmer", + "kir": "Kirgisisch", + "kur": "Kurdisch", + "lao": "Laotisch", + "lat": "Latein", + "mar": "Marathi", + "mlt": "Maltesisch", + "mon": "Mongolisch", + "mri": "Maori", + "msa": "Malaiisch", + "mya": "Myanmar", + "nep": "Nepalesisch", + "nno": "Norwegisch Nynorsk", + "ori": "Oriya", + "pan": "Punjabi", + "que": "Quechua", + "sin": "Sinhala", + "snd": "Sindhi", + "sqi": "Albanisch", + "srp": "Serbisch", + "srp_latn": "Serbisch (lateinisch)", + "sun": "Sundanesisch", + "swa": "Swahili", + "syr": "Syrisch", + "tam": "Tamil", + "tel": "Telugu", + "tgk": "Tadschikisch", + "tgl": "Tagalog", + "tir": "Tigrinya", + "ton": "Tonga", + "uig": "Uigurisch", + "urd": "Urdu", + "uzb": "Usbekisch", + "uzb_cyrl": "Usbekisch (kyrillisch)", + "yid": "Jiddisch", + "yor": "Yoruba" + }, "cookieBanner": { "popUp": { "title": "Wie wir Cookies verwenden", diff --git a/frontend/public/locales/en-US/translation.json b/frontend/public/locales/en-US/translation.json index 1b258a824..fc30a54ed 100644 --- a/frontend/public/locales/en-US/translation.json +++ b/frontend/public/locales/en-US/translation.json @@ -1531,6 +1531,115 @@ "desc": "View and test the Stirling PDF API endpoints", "tags": "api,documentation,swagger,endpoints,development" }, + "lang": { + "eng": "English", + "fra": "French", + "deu": "German", + "spa": "Spanish", + "ita": "Italian", + "por": "Portuguese", + "rus": "Russian", + "chi_sim": "Chinese (Simplified)", + "chi_sim_vert": "Chinese (Simplified, Vertical)", + "chi_tra": "Chinese (Traditional)", + "chi_tra_vert": "Chinese (Traditional, Vertical)", + "jpn": "Japanese", + "jpn_vert": "Japanese (Vertical)", + "kor": "Korean", + "kor_vert": "Korean (Vertical)", + "ara": "Arabic", + "hin": "Hindi", + "nld": "Dutch", + "ces": "Czech", + "pol": "Polish", + "tur": "Turkish", + "ukr": "Ukrainian", + "vie": "Vietnamese", + "swe": "Swedish", + "nor": "Norwegian", + "fin": "Finnish", + "dan": "Danish", + "ell": "Greek", + "heb": "Hebrew", + "hun": "Hungarian", + "bul": "Bulgarian", + "ron": "Romanian", + "hrv": "Croatian", + "slk": "Slovak", + "ind": "Indonesian", + "tha": "Thai", + "slv": "Slovenian", + "lav": "Latvian", + "lit": "Lithuanian", + "est": "Estonian", + "cat": "Catalan", + "eus": "Basque", + "glg": "Galician", + "oci": "Occitan", + "afr": "Afrikaans", + "amh": "Amharic", + "asm": "Assamese", + "aze": "Azerbaijani", + "aze_cyrl": "Azerbaijani (Cyrillic)", + "bel": "Belarusian", + "ben": "Bengali", + "bod": "Tibetan", + "bos": "Bosnian", + "bre": "Breton", + "ceb": "Cebuano", + "chr": "Cherokee", + "cym": "Welsh", + "dzo": "Dzongkha", + "epo": "Esperanto", + "equ": "Math / equation detection", + "fas": "Persian", + "fil": "Filipino", + "fry": "Western Frisian", + "gle": "Irish", + "guj": "Gujarati", + "hat": "Haitian Creole", + "iku": "Inuktitut", + "jav": "Javanese", + "kan": "Kannada", + "kaz": "Kazakh", + "kaz_cyrl": "Kazakh (Cyrillic)", + "khm": "Khmer", + "kir": "Kyrgyz", + "kur": "Kurdish", + "lao": "Lao", + "lat": "Latin", + "mar": "Marathi", + "mlt": "Maltese", + "mon": "Mongolian", + "mri": "Maori", + "msa": "Malay", + "mya": "Myanmar", + "nep": "Nepali", + "nno": "Norwegian Nynorsk", + "ori": "Oriya", + "pan": "Punjabi", + "que": "Quechua", + "sin": "Sinhala", + "snd": "Sindhi", + "sqi": "Albanian", + "srp": "Serbian", + "srp_latn": "Serbian (Latin)", + "sun": "Sundanese", + "swa": "Swahili", + "syr": "Syriac", + "tam": "Tamil", + "tel": "Telugu", + "tgk": "Tajik", + "tgl": "Tagalog", + "tir": "Tigrinya", + "ton": "Tonga", + "uig": "Uyghur", + "urd": "Urdu", + "uzb": "Uzbek", + "uzb_cyrl": "Uzbek (Cyrillic)", + "yid": "Yiddish", + "yor": "Yoruba" + }, "cookieBanner": { "popUp": { "title": "How we use Cookies", diff --git a/frontend/src/components/tools/ocr/OCRSettings.tsx b/frontend/src/components/tools/ocr/OCRSettings.tsx new file mode 100644 index 000000000..8a6abd2e1 --- /dev/null +++ b/frontend/src/components/tools/ocr/OCRSettings.tsx @@ -0,0 +1,161 @@ +import React, { useState, useEffect } from 'react'; +import { Stack, Select, MultiSelect, Text, Loader } from '@mantine/core'; +import { useTranslation } from 'react-i18next'; + +export interface OCRParameters { + languages: string[]; + ocrType: string; + ocrRenderType: string; + additionalOptions: string[]; +} + +interface OCRSettingsProps { + parameters: OCRParameters; + onParameterChange: (key: keyof OCRParameters, value: any) => void; + disabled?: boolean; +} + +const OCRSettings: React.FC = ({ + parameters, + onParameterChange, + disabled = false +}) => { + const { t } = useTranslation(); + const [availableLanguages, setAvailableLanguages] = useState<{value: string, label: string}[]>([]); + const [isLoadingLanguages, setIsLoadingLanguages] = useState(true); + + // Define the additional options available + const additionalOptionsData = [ + { value: 'sidecar', label: 'Create sidecar text file' }, + { value: 'deskew', label: 'Deskew pages' }, + { value: 'clean', label: 'Clean input file' }, + { value: 'cleanFinal', label: 'Clean final output' }, + { value: 'removeImagesAfter', label: 'Remove images after OCR' }, + ]; + + useEffect(() => { + // Fetch available languages from backend + const fetchLanguages = async () => { + console.log('[OCR Languages] Starting language fetch...'); + const url = '/api/v1/ui-data/ocr-pdf'; + console.log('[OCR Languages] Fetching from URL:', url); + + try { + const response = await fetch(url); + console.log('[OCR Languages] Response received:', { + status: response.status, + statusText: response.statusText, + ok: response.ok, + headers: Object.fromEntries(response.headers.entries()) + }); + + if (response.ok) { + const data: { languages: string[] } = await response.json(); + const languages = data.languages; + console.log('[OCR Languages] Raw response data:', languages); + console.log('[OCR Languages] Response type:', typeof languages, 'Array?', Array.isArray(languages)); + + const languageOptions = languages.map(lang => { + // Try to get the translated language name, fallback to capitalized code + const translatedName = t(`lang.${lang}`); + const displayName = translatedName; + + console.log(`[OCR Languages] Language mapping: ${lang} -> ${displayName} (translated: ${!!translatedName})`); + + return { + value: lang, + label: displayName + }; + }); + console.log('[OCR Languages] Transformed language options:', languageOptions); + + setAvailableLanguages(languageOptions); + console.log('[OCR Languages] Successfully set', languageOptions.length, 'languages'); + } else { + console.error('[OCR Languages] Response not OK:', response.status, response.statusText); + const errorText = await response.text(); + console.error('[OCR Languages] Error response body:', errorText); + } + } catch (error) { + console.error('[OCR Languages] Fetch failed with error:', error); + console.error('[OCR Languages] Error details:', { + name: error instanceof Error ? error.name : 'Unknown', + message: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined + }); + } finally { + setIsLoadingLanguages(false); + console.log('[OCR Languages] Language loading completed'); + } + }; + + fetchLanguages(); + }, [t]); // Add t to dependencies since we're using it in the effect + + return ( + + OCR Configuration + + {isLoadingLanguages ? ( +
+ + Loading available languages... +
+ ) : ( + onParameterChange('ocrType', value || 'skip-text')} + data={[ + { value: 'skip-text', label: 'Auto (skip text layers)' }, + { value: 'force-ocr', label: 'Force OCR - Process all pages' }, + { value: 'Normal', label: 'Normal - Error if text exists' }, + ]} + disabled={disabled} + /> + +