diff --git a/.yarn/patches/tesseract.js-npm-6.0.1-2562a7e46d.patch b/.yarn/patches/tesseract.js-npm-6.0.1-2562a7e46d.patch
new file mode 100644
index 0000000000..0cb156ee99
--- /dev/null
+++ b/.yarn/patches/tesseract.js-npm-6.0.1-2562a7e46d.patch
@@ -0,0 +1,348 @@
+diff --git a/src/constants/languages.d.ts b/src/constants/languages.d.ts
+new file mode 100644
+index 0000000000000000000000000000000000000000..6a2ba5086187622b8ca8887bcc7406018fba8a89
+--- /dev/null
++++ b/src/constants/languages.d.ts
+@@ -0,0 +1,43 @@
++/**
++ * Languages with existing tesseract traineddata
++ * https://tesseract-ocr.github.io/tessdoc/Data-Files#data-files-for-version-400-november-29-2016
++ */
++
++// Define the language codes as string literals
++type LanguageCode =
++ | 'afr' | 'amh' | 'ara' | 'asm' | 'aze' | 'aze_cyrl' | 'bel' | 'ben' | 'bod' | 'bos'
++ | 'bul' | 'cat' | 'ceb' | 'ces' | 'chi_sim' | 'chi_tra' | 'chr' | 'cym' | 'dan' | 'deu'
++ | 'dzo' | 'ell' | 'eng' | 'enm' | 'epo' | 'est' | 'eus' | 'fas' | 'fin' | 'fra'
++ | 'frk' | 'frm' | 'gle' | 'glg' | 'grc' | 'guj' | 'hat' | 'heb' | 'hin' | 'hrv'
++ | 'hun' | 'iku' | 'ind' | 'isl' | 'ita' | 'ita_old' | 'jav' | 'jpn' | 'kan' | 'kat'
++ | 'kat_old' | 'kaz' | 'khm' | 'kir' | 'kor' | 'kur' | 'lao' | 'lat' | 'lav' | 'lit'
++ | 'mal' | 'mar' | 'mkd' | 'mlt' | 'msa' | 'mya' | 'nep' | 'nld' | 'nor' | 'ori'
++ | 'pan' | 'pol' | 'por' | 'pus' | 'ron' | 'rus' | 'san' | 'sin' | 'slk' | 'slv'
++ | 'spa' | 'spa_old' | 'sqi' | 'srp' | 'srp_latn' | 'swa' | 'swe' | 'syr' | 'tam' | 'tel'
++ | 'tgk' | 'tgl' | 'tha' | 'tir' | 'tur' | 'uig' | 'ukr' | 'urd' | 'uzb' | 'uzb_cyrl'
++ | 'vie' | 'yid';
++
++// Define the language keys as string literals
++type LanguageKey =
++ | 'AFR' | 'AMH' | 'ARA' | 'ASM' | 'AZE' | 'AZE_CYRL' | 'BEL' | 'BEN' | 'BOD' | 'BOS'
++ | 'BUL' | 'CAT' | 'CEB' | 'CES' | 'CHI_SIM' | 'CHI_TRA' | 'CHR' | 'CYM' | 'DAN' | 'DEU'
++ | 'DZO' | 'ELL' | 'ENG' | 'ENM' | 'EPO' | 'EST' | 'EUS' | 'FAS' | 'FIN' | 'FRA'
++ | 'FRK' | 'FRM' | 'GLE' | 'GLG' | 'GRC' | 'GUJ' | 'HAT' | 'HEB' | 'HIN' | 'HRV'
++ | 'HUN' | 'IKU' | 'IND' | 'ISL' | 'ITA' | 'ITA_OLD' | 'JAV' | 'JPN' | 'KAN' | 'KAT'
++ | 'KAT_OLD' | 'KAZ' | 'KHM' | 'KIR' | 'KOR' | 'KUR' | 'LAO' | 'LAT' | 'LAV' | 'LIT'
++ | 'MAL' | 'MAR' | 'MKD' | 'MLT' | 'MSA' | 'MYA' | 'NEP' | 'NLD' | 'NOR' | 'ORI'
++ | 'PAN' | 'POL' | 'POR' | 'PUS' | 'RON' | 'RUS' | 'SAN' | 'SIN' | 'SLK' | 'SLV'
++ | 'SPA' | 'SPA_OLD' | 'SQI' | 'SRP' | 'SRP_LATN' | 'SWA' | 'SWE' | 'SYR' | 'TAM' | 'TEL'
++ | 'TGK' | 'TGL' | 'THA' | 'TIR' | 'TUR' | 'UIG' | 'UKR' | 'URD' | 'UZB' | 'UZB_CYRL'
++ | 'VIE' | 'YID';
++
++// Create a mapped type to ensure each key maps to its specific value
++type LanguagesMap = {
++ [K in LanguageKey]: LanguageCode;
++};
++
++// Declare the exported constant with the specific type
++export const LANGUAGES: LanguagesMap;
++
++// Export the individual types for use in other modules
++export type { LanguageCode, LanguageKey, LanguagesMap };
+\ No newline at end of file
+diff --git a/src/index.d.ts b/src/index.d.ts
+index 1f5a9c8094fe4de7983467f9efb43bdb4de535f2..16dc95cf68663673e37e189b719cb74897b7735f 100644
+--- a/src/index.d.ts
++++ b/src/index.d.ts
+@@ -1,31 +1,74 @@
++// Import the languages types
++import { LanguagesMap } from "./constants/languages";
++
++///
++
+ declare namespace Tesseract {
+- function createScheduler(): Scheduler
+- function createWorker(langs?: string | string[] | Lang[], oem?: OEM, options?: Partial, config?: string | Partial): Promise
+- function setLogging(logging: boolean): void
+- function recognize(image: ImageLike, langs?: string, options?: Partial): Promise
+- function detect(image: ImageLike, options?: Partial): any
++ function createScheduler(): Scheduler;
++ function createWorker(
++ langs?: LanguageCode | LanguageCode[] | Lang[],
++ oem?: OEM,
++ options?: Partial,
++ config?: string | Partial
++ ): Promise;
++ function setLogging(logging: boolean): void;
++ function recognize(
++ image: ImageLike,
++ langs?: LanguageCode,
++ options?: Partial
++ ): Promise;
++ function detect(image: ImageLike, options?: Partial): any;
++
++ // Export languages constant
++ const languages: LanguagesMap;
++
++ type LanguageCode = import("./constants/languages").LanguageCode;
++ type LanguageKey = import("./constants/languages").LanguageKey;
+
+ interface Scheduler {
+- addWorker(worker: Worker): string
+- addJob(action: 'recognize', ...args: Parameters): Promise
+- addJob(action: 'detect', ...args: Parameters): Promise
+- terminate(): Promise
+- getQueueLen(): number
+- getNumWorkers(): number
++ addWorker(worker: Worker): string;
++ addJob(
++ action: "recognize",
++ ...args: Parameters
++ ): Promise;
++ addJob(
++ action: "detect",
++ ...args: Parameters
++ ): Promise;
++ terminate(): Promise;
++ getQueueLen(): number;
++ getNumWorkers(): number;
+ }
+
+ interface Worker {
+- load(jobId?: string): Promise
+- writeText(path: string, text: string, jobId?: string): Promise
+- readText(path: string, jobId?: string): Promise
+- removeText(path: string, jobId?: string): Promise
+- FS(method: string, args: any[], jobId?: string): Promise
+- reinitialize(langs?: string | Lang[], oem?: OEM, config?: string | Partial, jobId?: string): Promise
+- setParameters(params: Partial, jobId?: string): Promise
+- getImage(type: imageType): string
+- recognize(image: ImageLike, options?: Partial, output?: Partial, jobId?: string): Promise
+- detect(image: ImageLike, jobId?: string): Promise
+- terminate(jobId?: string): Promise
++ load(jobId?: string): Promise;
++ writeText(
++ path: string,
++ text: string,
++ jobId?: string
++ ): Promise;
++ readText(path: string, jobId?: string): Promise;
++ removeText(path: string, jobId?: string): Promise;
++ FS(method: string, args: any[], jobId?: string): Promise;
++ reinitialize(
++ langs?: string | Lang[],
++ oem?: OEM,
++ config?: string | Partial,
++ jobId?: string
++ ): Promise;
++ setParameters(
++ params: Partial,
++ jobId?: string
++ ): Promise;
++ getImage(type: imageType): string;
++ recognize(
++ image: ImageLike,
++ options?: Partial,
++ output?: Partial,
++ jobId?: string
++ ): Promise;
++ detect(image: ImageLike, jobId?: string): Promise;
++ terminate(jobId?: string): Promise;
+ }
+
+ interface Lang {
+@@ -34,43 +77,43 @@ declare namespace Tesseract {
+ }
+
+ interface InitOptions {
+- load_system_dawg: string
+- load_freq_dawg: string
+- load_unambig_dawg: string
+- load_punc_dawg: string
+- load_number_dawg: string
+- load_bigram_dawg: string
+- }
+-
+- type LoggerMessage = {
+- jobId: string
+- progress: number
+- status: string
+- userJobId: string
+- workerId: string
++ load_system_dawg: string;
++ load_freq_dawg: string;
++ load_unambig_dawg: string;
++ load_punc_dawg: string;
++ load_number_dawg: string;
++ load_bigram_dawg: string;
+ }
+-
++
++ type LoggerMessage = {
++ jobId: string;
++ progress: number;
++ status: string;
++ userJobId: string;
++ workerId: string;
++ };
++
+ interface WorkerOptions {
+- corePath: string
+- langPath: string
+- cachePath: string
+- dataPath: string
+- workerPath: string
+- cacheMethod: string
+- workerBlobURL: boolean
+- gzip: boolean
+- legacyLang: boolean
+- legacyCore: boolean
+- logger: (arg: LoggerMessage) => void,
+- errorHandler: (arg: any) => void
++ corePath: string;
++ langPath: string;
++ cachePath: string;
++ dataPath: string;
++ workerPath: string;
++ cacheMethod: string;
++ workerBlobURL: boolean;
++ gzip: boolean;
++ legacyLang: boolean;
++ legacyCore: boolean;
++ logger: (arg: LoggerMessage) => void;
++ errorHandler: (arg: any) => void;
+ }
+ interface WorkerParams {
+- tessedit_pageseg_mode: PSM
+- tessedit_char_whitelist: string
+- tessedit_char_blacklist: string
+- preserve_interword_spaces: string
+- user_defined_dpi: string
+- [propName: string]: any
++ tessedit_pageseg_mode: PSM;
++ tessedit_char_whitelist: string;
++ tessedit_char_blacklist: string;
++ preserve_interword_spaces: string;
++ user_defined_dpi: string;
++ [propName: string]: any;
+ }
+ interface OutputFormats {
+ text: boolean;
+@@ -88,36 +131,36 @@ declare namespace Tesseract {
+ debug: boolean;
+ }
+ interface RecognizeOptions {
+- rectangle: Rectangle
+- pdfTitle: string
+- pdfTextOnly: boolean
+- rotateAuto: boolean
+- rotateRadians: number
++ rectangle: Rectangle;
++ pdfTitle: string;
++ pdfTextOnly: boolean;
++ rotateAuto: boolean;
++ rotateRadians: number;
+ }
+ interface ConfigResult {
+- jobId: string
+- data: any
++ jobId: string;
++ data: any;
+ }
+ interface RecognizeResult {
+- jobId: string
+- data: Page
++ jobId: string;
++ data: Page;
+ }
+ interface DetectResult {
+- jobId: string
+- data: DetectData
++ jobId: string;
++ data: DetectData;
+ }
+ interface DetectData {
+- tesseract_script_id: number | null
+- script: string | null
+- script_confidence: number | null
+- orientation_degrees: number | null
+- orientation_confidence: number | null
++ tesseract_script_id: number | null;
++ script: string | null;
++ script_confidence: number | null;
++ orientation_degrees: number | null;
++ orientation_confidence: number | null;
+ }
+ interface Rectangle {
+- left: number
+- top: number
+- width: number
+- height: number
++ left: number;
++ top: number;
++ width: number;
++ height: number;
+ }
+ enum OEM {
+ TESSERACT_ONLY,
+@@ -126,28 +169,36 @@ declare namespace Tesseract {
+ DEFAULT,
+ }
+ enum PSM {
+- OSD_ONLY = '0',
+- AUTO_OSD = '1',
+- AUTO_ONLY = '2',
+- AUTO = '3',
+- SINGLE_COLUMN = '4',
+- SINGLE_BLOCK_VERT_TEXT = '5',
+- SINGLE_BLOCK = '6',
+- SINGLE_LINE = '7',
+- SINGLE_WORD = '8',
+- CIRCLE_WORD = '9',
+- SINGLE_CHAR = '10',
+- SPARSE_TEXT = '11',
+- SPARSE_TEXT_OSD = '12',
+- RAW_LINE = '13'
++ OSD_ONLY = "0",
++ AUTO_OSD = "1",
++ AUTO_ONLY = "2",
++ AUTO = "3",
++ SINGLE_COLUMN = "4",
++ SINGLE_BLOCK_VERT_TEXT = "5",
++ SINGLE_BLOCK = "6",
++ SINGLE_LINE = "7",
++ SINGLE_WORD = "8",
++ CIRCLE_WORD = "9",
++ SINGLE_CHAR = "10",
++ SPARSE_TEXT = "11",
++ SPARSE_TEXT_OSD = "12",
++ RAW_LINE = "13",
+ }
+ const enum imageType {
+ COLOR = 0,
+ GREY = 1,
+- BINARY = 2
++ BINARY = 2,
+ }
+- type ImageLike = string | HTMLImageElement | HTMLCanvasElement | HTMLVideoElement
+- | CanvasRenderingContext2D | File | Blob | Buffer | OffscreenCanvas;
++ type ImageLike =
++ | string
++ | HTMLImageElement
++ | HTMLCanvasElement
++ | HTMLVideoElement
++ | CanvasRenderingContext2D
++ | File
++ | Blob
++ | (typeof Buffer extends undefined ? never : Buffer)
++ | OffscreenCanvas;
+ interface Block {
+ paragraphs: Paragraph[];
+ text: string;
+@@ -179,7 +230,7 @@ declare namespace Tesseract {
+ text: string;
+ confidence: number;
+ baseline: Baseline;
+- rowAttributes: RowAttributes
++ rowAttributes: RowAttributes;
+ bbox: Bbox;
+ }
+ interface Paragraph {
diff --git a/package.json b/package.json
index 49f7340559..c734271de5 100644
--- a/package.json
+++ b/package.json
@@ -79,7 +79,7 @@
"officeparser": "^4.2.0",
"os-proxy-config": "^1.1.2",
"selection-hook": "^1.0.11",
- "tesseract.js": "^6.0.1",
+ "tesseract.js": "patch:tesseract.js@npm%3A6.0.1#~/.yarn/patches/tesseract.js-npm-6.0.1-2562a7e46d.patch",
"turndown": "7.2.0"
},
"devDependencies": {
@@ -293,7 +293,8 @@
"pdf-parse@npm:1.1.1": "patch:pdf-parse@npm%3A1.1.1#~/.yarn/patches/pdf-parse-npm-1.1.1-04a6109b2a.patch",
"pkce-challenge@npm:^4.1.0": "patch:pkce-challenge@npm%3A4.1.0#~/.yarn/patches/pkce-challenge-npm-4.1.0-fbc51695a3.patch",
"undici": "6.21.2",
- "vite": "npm:rolldown-vite@latest"
+ "vite": "npm:rolldown-vite@latest",
+ "tesseract.js@npm:*": "patch:tesseract.js@npm%3A6.0.1#~/.yarn/patches/tesseract.js-npm-6.0.1-2562a7e46d.patch"
},
"packageManager": "yarn@4.9.1",
"lint-staged": {
diff --git a/src/main/services/ocr/tesseract/TesseractService.ts b/src/main/services/ocr/tesseract/TesseractService.ts
index 77225d4afe..d354f85428 100644
--- a/src/main/services/ocr/tesseract/TesseractService.ts
+++ b/src/main/services/ocr/tesseract/TesseractService.ts
@@ -5,118 +5,13 @@ import { ImageFileMetadata, isImageFile, OcrResult, SupportedOcrFile } from '@ty
import { app } from 'electron'
import fs from 'fs'
import path from 'path'
-import Tesseract, { createWorker } from 'tesseract.js'
+import Tesseract, { createWorker, LanguageCode } from 'tesseract.js'
const logger = loggerService.withContext('TesseractService')
-// const languageCodeMap: Record = {
-// 'af-za': 'afr',
-// 'am-et': 'amh',
-// 'ar-sa': 'ara',
-// 'as-in': 'asm',
-// 'az-az': 'aze',
-// 'az-cyrl-az': 'aze_cyrl',
-// 'be-by': 'bel',
-// 'bn-bd': 'ben',
-// 'bo-cn': 'bod',
-// 'bs-ba': 'bos',
-// 'bg-bg': 'bul',
-// 'ca-es': 'cat',
-// 'ceb-ph': 'ceb',
-// 'cs-cz': 'ces',
-// 'zh-cn': 'chi_sim',
-// 'zh-tw': 'chi_tra',
-// 'chr-us': 'chr',
-// 'cy-gb': 'cym',
-// 'da-dk': 'dan',
-// 'de-de': 'deu',
-// 'dz-bt': 'dzo',
-// 'el-gr': 'ell',
-// 'en-us': 'eng',
-// 'enm-gb': 'enm',
-// 'eo-world': 'epo',
-// 'et-ee': 'est',
-// 'eu-es': 'eus',
-// 'fa-ir': 'fas',
-// 'fi-fi': 'fin',
-// 'fr-fr': 'fra',
-// 'frk-de': 'frk',
-// 'frm-fr': 'frm',
-// 'ga-ie': 'gle',
-// 'gl-es': 'glg',
-// 'grc-gr': 'grc',
-// 'gu-in': 'guj',
-// 'ht-ht': 'hat',
-// 'he-il': 'heb',
-// 'hi-in': 'hin',
-// 'hr-hr': 'hrv',
-// 'hu-hu': 'hun',
-// 'iu-ca': 'iku',
-// 'id-id': 'ind',
-// 'is-is': 'isl',
-// 'it-it': 'ita',
-// 'ita-it': 'ita_old',
-// 'jv-id': 'jav',
-// 'ja-jp': 'jpn',
-// 'kn-in': 'kan',
-// 'ka-ge': 'kat',
-// 'kat-ge': 'kat_old',
-// 'kk-kz': 'kaz',
-// 'km-kh': 'khm',
-// 'ky-kg': 'kir',
-// 'ko-kr': 'kor',
-// 'ku-tr': 'kur',
-// 'la-la': 'lao',
-// 'la-va': 'lat',
-// 'lv-lv': 'lav',
-// 'lt-lt': 'lit',
-// 'ml-in': 'mal',
-// 'mr-in': 'mar',
-// 'mk-mk': 'mkd',
-// 'mt-mt': 'mlt',
-// 'ms-my': 'msa',
-// 'my-mm': 'mya',
-// 'ne-np': 'nep',
-// 'nl-nl': 'nld',
-// 'no-no': 'nor',
-// 'or-in': 'ori',
-// 'pa-in': 'pan',
-// 'pl-pl': 'pol',
-// 'pt-pt': 'por',
-// 'ps-af': 'pus',
-// 'ro-ro': 'ron',
-// 'ru-ru': 'rus',
-// 'sa-in': 'san',
-// 'si-lk': 'sin',
-// 'sk-sk': 'slk',
-// 'sl-si': 'slv',
-// 'es-es': 'spa',
-// 'spa-es': 'spa_old',
-// 'sq-al': 'sqi',
-// 'sr-rs': 'srp',
-// 'sr-latn-rs': 'srp_latn',
-// 'sw-tz': 'swa',
-// 'sv-se': 'swe',
-// 'syr-sy': 'syr',
-// 'ta-in': 'tam',
-// 'te-in': 'tel',
-// 'tg-tj': 'tgk',
-// 'tl-ph': 'tgl',
-// 'th-th': 'tha',
-// 'ti-er': 'tir',
-// 'tr-tr': 'tur',
-// 'ug-cn': 'uig',
-// 'uk-ua': 'ukr',
-// 'ur-pk': 'urd',
-// 'uz-uz': 'uzb',
-// 'uz-cyrl-uz': 'uzb_cyrl',
-// 'vi-vn': 'vie',
-// 'yi-us': 'yid'
-// }
-
// config
const MB_SIZE_THRESHOLD = 50
-const tesseractLangs = ['chi_sim', 'chi_tra', 'eng']
+const tesseractLangs = ['chi_sim', 'chi_tra', 'eng'] satisfies LanguageCode[]
enum TesseractLangsDownloadUrl {
CN = 'https://gitcode.com/beyondkmp/tessdata/releases/download/4.1.0/',
GLOBAL = 'https://github.com/tesseract-ocr/tessdata/raw/main/'
diff --git a/src/renderer/src/config/ocr.ts b/src/renderer/src/config/ocr.ts
index 1187b49dc0..b899cbb5f0 100644
--- a/src/renderer/src/config/ocr.ts
+++ b/src/renderer/src/config/ocr.ts
@@ -1,14 +1,31 @@
-import { BuiltinOcrProvider, ImageOcrProvider, OcrProviderCapability } from '@renderer/types'
+import {
+ BuiltinOcrProvider,
+ BuiltinOcrProviderId,
+ ImageOcrProvider,
+ OcrProviderCapability,
+ OcrTesseractProvider
+} from '@renderer/types'
-const tesseract: BuiltinOcrProvider & ImageOcrProvider = {
+const tesseract: BuiltinOcrProvider & ImageOcrProvider & OcrTesseractProvider = {
id: 'tesseract',
name: 'Tesseract',
capabilities: {
image: true
+ },
+ config: {
+ langs: {
+ chi_sim: true,
+ chi_tra: true,
+ eng: true
+ }
}
-} as const
+} as const satisfies OcrTesseractProvider
-export const BUILTIN_OCR_PROVIDERS: BuiltinOcrProvider[] = [tesseract] as const
+export const BUILTIN_OCR_PROVIDERS_MAP = {
+ tesseract
+} as const satisfies Record
+
+export const BUILTIN_OCR_PROVIDERS: BuiltinOcrProvider[] = Object.values(BUILTIN_OCR_PROVIDERS_MAP)
export const DEFAULT_OCR_PROVIDER = {
image: tesseract
diff --git a/src/renderer/src/hooks/useOcrProvider.ts b/src/renderer/src/hooks/useOcrProvider.ts
new file mode 100644
index 0000000000..ce2eb5b8fc
--- /dev/null
+++ b/src/renderer/src/hooks/useOcrProvider.ts
@@ -0,0 +1,84 @@
+import { loggerService } from '@logger'
+import { BUILTIN_OCR_PROVIDERS_MAP } from '@renderer/config/ocr'
+import { useAppSelector } from '@renderer/store'
+import { addOcrProvider, removeOcrProvider, updateOcrProviderConfig } from '@renderer/store/ocr'
+import { isBuiltinOcrProviderId, OcrProvider, OcrProviderConfig } from '@renderer/types'
+import { useTranslation } from 'react-i18next'
+import { useDispatch } from 'react-redux'
+
+const logger = loggerService.withContext('useOcrProvider')
+
+export const useOcrProviders = () => {
+ const providers = useAppSelector((state) => state.ocr.providers)
+ const dispatch = useDispatch()
+ const { t } = useTranslation()
+
+ /**
+ * 添加一个新的OCR服务提供者
+ * @param provider - OCR提供者对象,包含id和其他配置信息
+ * @throws {Error} 当尝试添加一个已存在ID的提供者时抛出错误
+ */
+ const addProvider = (provider: OcrProvider) => {
+ if (providers.some((p) => p.id === provider.id)) {
+ const msg = `Provider with id ${provider.id} already exists`
+ logger.error(msg)
+ window.message.error(t('ocr.error.provider.existing'))
+ throw new Error(msg)
+ }
+ dispatch(addOcrProvider(provider))
+ }
+
+ /**
+ * 移除一个OCR服务提供者
+ * @param id - 要移除的OCR提供者ID
+ * @throws {Error} 当尝试移除一个内置提供商时抛出错误
+ */
+ const removeProvider = (id: string) => {
+ if (isBuiltinOcrProviderId(id)) {
+ const msg = `Cannot remove builtin provider ${id}`
+ logger.error(msg)
+ window.message.error(t('ocr.error.provider.cannot_remove_builtin'))
+ throw new Error(msg)
+ }
+
+ dispatch(removeOcrProvider(id))
+ }
+
+ return { providers, addProvider, removeProvider }
+}
+
+export const useOcrProvider = (id: string) => {
+ const { t } = useTranslation()
+ const dispatch = useDispatch()
+ const { providers, addProvider } = useOcrProviders()
+ let provider = providers.find((p) => p.id === id)
+
+ // safely fallback
+ if (!provider) {
+ logger.error(`Ocr Provider ${id} not found`)
+ window.message.error(t('ocr.error.provider.not_found'))
+ if (isBuiltinOcrProviderId(id)) {
+ try {
+ addProvider(BUILTIN_OCR_PROVIDERS_MAP[id])
+ } catch (e) {
+ logger.warn(`Add ${BUILTIN_OCR_PROVIDERS_MAP[id].name} failed. Just use temp provider from config.`)
+ window.message.warning(t('ocr.warning.provider.fallback', { name: BUILTIN_OCR_PROVIDERS_MAP[id].name }))
+ } finally {
+ provider = BUILTIN_OCR_PROVIDERS_MAP[id]
+ }
+ } else {
+ logger.warn(`Fallback to tesseract`)
+ window.message.warning(t('ocr.warning.provider.fallback', { name: 'Tesseract' }))
+ provider = BUILTIN_OCR_PROVIDERS_MAP.tesseract
+ }
+ }
+
+ const updateConfig = (update: Partial) => {
+ dispatch(updateOcrProviderConfig({ id: provider.id, update }))
+ }
+
+ return {
+ provider,
+ updateConfig
+ }
+}
diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json
index e84b350625..2acbf399c8 100644
--- a/src/renderer/src/i18n/locales/en-us.json
+++ b/src/renderer/src/i18n/locales/en-us.json
@@ -1566,12 +1566,23 @@
},
"ocr": {
"error": {
+ "provider": {
+ "cannot_remove_builtin": "Cannot delete built-in provider",
+ "existing": "The provider already exists",
+ "not_found": "OCR provider does not exist",
+ "update_failed": "Failed to update configuration"
+ },
"unknown": "An error occurred during the OCR process"
},
"file": {
"not_supported": "Unsupported file type {{type}}"
},
- "processing": "OCR processing..."
+ "processing": "OCR processing...",
+ "warning": {
+ "provider": {
+ "fallback": "Reverted to {{name}}, which may cause issues"
+ }
+ }
},
"ollama": {
"keep_alive_time": {
@@ -3498,6 +3509,10 @@
"error": {
"provider_not_found": "The provider does not exist"
},
+ "tesseract": {
+ "langs": "Supported languages",
+ "temp_tooltip": "Currently only Chinese and English are supported"
+ },
"title": "Image"
},
"image_provider": "OCR service provider",
diff --git a/src/renderer/src/i18n/locales/ja-jp.json b/src/renderer/src/i18n/locales/ja-jp.json
index 260e3fd258..92a3742373 100644
--- a/src/renderer/src/i18n/locales/ja-jp.json
+++ b/src/renderer/src/i18n/locales/ja-jp.json
@@ -1566,12 +1566,23 @@
},
"ocr": {
"error": {
+ "provider": {
+ "cannot_remove_builtin": "組み込みプロバイダーは削除できません",
+ "existing": "プロバイダーはすでに存在します",
+ "not_found": "OCRプロバイダーが存在しません",
+ "update_failed": "更新構成に失敗しました"
+ },
"unknown": "OCR処理中にエラーが発生しました"
},
"file": {
"not_supported": "サポートされていないファイルタイプ {{type}}"
},
- "processing": "OCR処理中..."
+ "processing": "OCR処理中...",
+ "warning": {
+ "provider": {
+ "fallback": "{{name}} に戻されました。これにより問題が発生する可能性があります。"
+ }
+ }
},
"ollama": {
"keep_alive_time": {
@@ -3498,6 +3509,10 @@
"error": {
"provider_not_found": "該提供者は存在しません"
},
+ "tesseract": {
+ "langs": "サポートされている言語",
+ "temp_tooltip": "現在のところ、中国語と英語のみをサポートしています"
+ },
"title": "画像"
},
"image_provider": "OCRサービスプロバイダー",
diff --git a/src/renderer/src/i18n/locales/ru-ru.json b/src/renderer/src/i18n/locales/ru-ru.json
index ffdddd710d..1cc850db67 100644
--- a/src/renderer/src/i18n/locales/ru-ru.json
+++ b/src/renderer/src/i18n/locales/ru-ru.json
@@ -1566,12 +1566,23 @@
},
"ocr": {
"error": {
+ "provider": {
+ "cannot_remove_builtin": "Не удается удалить встроенного поставщика",
+ "existing": "Поставщик уже существует",
+ "not_found": "Поставщик OCR отсутствует",
+ "update_failed": "Обновление конфигурации не удалось"
+ },
"unknown": "Произошла ошибка в процессе распознавания текста"
},
"file": {
"not_supported": "Неподдерживаемый тип файла {{type}}"
},
- "processing": "Обработка OCR..."
+ "processing": "Обработка OCR...",
+ "warning": {
+ "provider": {
+ "fallback": "Возвращено к {{name}}, это может вызвать проблемы"
+ }
+ }
},
"ollama": {
"keep_alive_time": {
@@ -3498,6 +3509,10 @@
"error": {
"provider_not_found": "Поставщик не существует"
},
+ "tesseract": {
+ "langs": "Поддерживаемые языки",
+ "temp_tooltip": "На данный момент поддерживаются только китайский и английский языки"
+ },
"title": "Изображение"
},
"image_provider": "Поставщик услуг OCR",
diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json
index 5c196d4aaf..90acff50ac 100644
--- a/src/renderer/src/i18n/locales/zh-cn.json
+++ b/src/renderer/src/i18n/locales/zh-cn.json
@@ -1566,12 +1566,23 @@
},
"ocr": {
"error": {
- "unknown": "OCR过程发生错误"
+ "provider": {
+ "cannot_remove_builtin": "不能删除内置提供商",
+ "existing": "提供商已存在",
+ "not_found": "OCR 提供商不存在",
+ "update_failed": "更新配置失败"
+ },
+ "unknown": "OCR 过程发生错误"
},
"file": {
"not_supported": "不支持的文件类型 {{type}}"
},
- "processing": "OCR 处理中..."
+ "processing": "OCR 处理中...",
+ "warning": {
+ "provider": {
+ "fallback": "已回退到 {{name}},这可能导致问题"
+ }
+ }
},
"ollama": {
"keep_alive_time": {
@@ -3498,6 +3509,10 @@
"error": {
"provider_not_found": "该提供商不存在"
},
+ "tesseract": {
+ "langs": "支持的语言",
+ "temp_tooltip": "目前暂时只支持中文和英文"
+ },
"title": "图片"
},
"image_provider": "OCR 服务提供商",
diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json
index 5e81d73688..b6f5456b17 100644
--- a/src/renderer/src/i18n/locales/zh-tw.json
+++ b/src/renderer/src/i18n/locales/zh-tw.json
@@ -1566,12 +1566,23 @@
},
"ocr": {
"error": {
+ "provider": {
+ "cannot_remove_builtin": "不能刪除內建提供者",
+ "existing": "提供商已存在",
+ "not_found": "OCR 提供商不存在",
+ "update_failed": "更新配置失敗"
+ },
"unknown": "OCR過程發生錯誤"
},
"file": {
"not_supported": "不支持的文件類型 {{type}}"
},
- "processing": "OCR 處理中..."
+ "processing": "OCR 處理中...",
+ "warning": {
+ "provider": {
+ "fallback": "已回退到 {{name}},這可能導致問題"
+ }
+ }
},
"ollama": {
"keep_alive_time": {
@@ -3498,6 +3509,10 @@
"error": {
"provider_not_found": "該提供商不存在"
},
+ "tesseract": {
+ "langs": "支援的語言",
+ "temp_tooltip": "目前暫時只支援中文和英文"
+ },
"title": "圖片"
},
"image_provider": "OCR 服務提供商",
diff --git a/src/renderer/src/i18n/translate/el-gr.json b/src/renderer/src/i18n/translate/el-gr.json
index 7a24908b09..4a8ef581a6 100644
--- a/src/renderer/src/i18n/translate/el-gr.json
+++ b/src/renderer/src/i18n/translate/el-gr.json
@@ -888,6 +888,9 @@
},
"history": {
"continue_chat": "Συνεχίστε το συνομιλημένο",
+ "error": {
+ "topic_not_found": "Το θέμα δεν υπάρχει"
+ },
"locate": {
"message": "Εφαρμογή στο μήνυμα"
},
@@ -1563,12 +1566,23 @@
},
"ocr": {
"error": {
+ "provider": {
+ "cannot_remove_builtin": "Δεν είναι δυνατή η διαγραφή του ενσωματωμένου παρόχου",
+ "existing": "Ο πάροχος υπηρεσιών υπάρχει ήδη",
+ "not_found": "Ο πάροχος OCR δεν υπάρχει",
+ "update_failed": "Αποτυχία ενημέρωσης της διαμόρφωσης"
+ },
"unknown": "Η διαδικασία OCR εμφάνισε σφάλμα"
},
"file": {
"not_supported": "Μη υποστηριζόμενος τύπος αρχείου {{type}}"
},
- "processing": "Η επεξεργασία OCR βρίσκεται σε εξέλιξη..."
+ "processing": "Η επεξεργασία OCR βρίσκεται σε εξέλιξη...",
+ "warning": {
+ "provider": {
+ "fallback": "Επαναφέρθηκε στο {{name}}, το οποίο μπορεί να προκαλέσει προβλήματα"
+ }
+ }
},
"ollama": {
"keep_alive_time": {
@@ -3495,6 +3509,10 @@
"error": {
"provider_not_found": "Ο πάροχος δεν υπάρχει"
},
+ "tesseract": {
+ "langs": "Υποστηριζόμενες γλώσσες",
+ "temp_tooltip": "Προς το παρόν υποστηρίζονται μόνο η κινεζική και η αγγλική γλώσσα"
+ },
"title": "Εικόνα"
},
"image_provider": "Πάροχοι υπηρεσιών OCR",
diff --git a/src/renderer/src/i18n/translate/es-es.json b/src/renderer/src/i18n/translate/es-es.json
index 8fd6b2e14e..e426fca943 100644
--- a/src/renderer/src/i18n/translate/es-es.json
+++ b/src/renderer/src/i18n/translate/es-es.json
@@ -888,6 +888,9 @@
},
"history": {
"continue_chat": "Continuar chat",
+ "error": {
+ "topic_not_found": "El tema no existe"
+ },
"locate": {
"message": "Localizar mensaje"
},
@@ -1563,12 +1566,23 @@
},
"ocr": {
"error": {
+ "provider": {
+ "cannot_remove_builtin": "No se puede eliminar el proveedor integrado",
+ "existing": "El proveedor ya existe",
+ "not_found": "El proveedor de OCR no existe",
+ "update_failed": "Actualización de la configuración fallida"
+ },
"unknown": "El proceso OCR ha fallado"
},
"file": {
"not_supported": "Tipo de archivo no compatible {{type}}"
},
- "processing": "Procesando OCR..."
+ "processing": "Procesando OCR...",
+ "warning": {
+ "provider": {
+ "fallback": "Se ha revertido a {{name}}, lo que podría causar problemas"
+ }
+ }
},
"ollama": {
"keep_alive_time": {
@@ -3495,6 +3509,10 @@
"error": {
"provider_not_found": "El proveedor no existe"
},
+ "tesseract": {
+ "langs": "Idiomas compatibles",
+ "temp_tooltip": "Actualmente solo se admiten chino e inglés."
+ },
"title": "Imagen"
},
"image_provider": "Proveedor de servicios OCR",
diff --git a/src/renderer/src/i18n/translate/fr-fr.json b/src/renderer/src/i18n/translate/fr-fr.json
index 510ded6983..ad64477c78 100644
--- a/src/renderer/src/i18n/translate/fr-fr.json
+++ b/src/renderer/src/i18n/translate/fr-fr.json
@@ -888,6 +888,9 @@
},
"history": {
"continue_chat": "Continuer la conversation",
+ "error": {
+ "topic_not_found": "Le sujet n'existe pas"
+ },
"locate": {
"message": "Localiser le message"
},
@@ -1563,12 +1566,23 @@
},
"ocr": {
"error": {
+ "provider": {
+ "cannot_remove_builtin": "Impossible de supprimer le fournisseur intégré",
+ "existing": "Le fournisseur existe déjà",
+ "not_found": "Le fournisseur OCR n'existe pas",
+ "update_failed": "Échec de la mise à jour de la configuration"
+ },
"unknown": "Une erreur s'est produite lors du processus OCR"
},
"file": {
"not_supported": "Type de fichier non pris en charge {{type}}"
},
- "processing": "Traitement OCR en cours..."
+ "processing": "Traitement OCR en cours...",
+ "warning": {
+ "provider": {
+ "fallback": "Revenu à {{name}}, ce qui pourrait entraîner des problèmes"
+ }
+ }
},
"ollama": {
"keep_alive_time": {
@@ -3495,6 +3509,10 @@
"error": {
"provider_not_found": "Ce fournisseur n'existe pas"
},
+ "tesseract": {
+ "langs": "Langues prises en charge",
+ "temp_tooltip": "Pour le moment, seuls le chinois et l'anglais sont pris en charge."
+ },
"title": "Image"
},
"image_provider": "Fournisseur de service OCR",
diff --git a/src/renderer/src/i18n/translate/pt-pt.json b/src/renderer/src/i18n/translate/pt-pt.json
index c5b1e28015..789f5163cd 100644
--- a/src/renderer/src/i18n/translate/pt-pt.json
+++ b/src/renderer/src/i18n/translate/pt-pt.json
@@ -888,6 +888,9 @@
},
"history": {
"continue_chat": "Continuar conversando",
+ "error": {
+ "topic_not_found": "Tópico inexistente"
+ },
"locate": {
"message": "Localizar mensagem"
},
@@ -1563,12 +1566,23 @@
},
"ocr": {
"error": {
+ "provider": {
+ "cannot_remove_builtin": "Não é possível excluir o provedor integrado",
+ "existing": "O provedor já existe",
+ "not_found": "O provedor OCR não existe",
+ "update_failed": "Falha ao atualizar a configuração"
+ },
"unknown": "O processo OCR apresentou um erro"
},
"file": {
"not_supported": "Tipo de arquivo não suportado {{type}}"
},
- "processing": "Processamento OCR em andamento..."
+ "processing": "Processamento OCR em andamento...",
+ "warning": {
+ "provider": {
+ "fallback": "Revertido para {{name}}, o que pode causar problemas"
+ }
+ }
},
"ollama": {
"keep_alive_time": {
@@ -3495,6 +3509,10 @@
"error": {
"provider_not_found": "O provedor não existe"
},
+ "tesseract": {
+ "langs": "Idiomas suportados",
+ "temp_tooltip": "No momento, apenas chinês e inglês são suportados."
+ },
"title": "Imagem"
},
"image_provider": "Provedor de serviços OCR",
diff --git a/src/renderer/src/pages/settings/DocProcessSettings/OcrImageProviderSettings.tsx b/src/renderer/src/pages/settings/DocProcessSettings/OcrImageSettings.tsx
similarity index 76%
rename from src/renderer/src/pages/settings/DocProcessSettings/OcrImageProviderSettings.tsx
rename to src/renderer/src/pages/settings/DocProcessSettings/OcrImageSettings.tsx
index ad150cc666..3efdf94fa0 100644
--- a/src/renderer/src/pages/settings/DocProcessSettings/OcrImageProviderSettings.tsx
+++ b/src/renderer/src/pages/settings/DocProcessSettings/OcrImageSettings.tsx
@@ -1,22 +1,32 @@
import { loggerService } from '@logger'
import { useAppSelector } from '@renderer/store'
import { setImageOcrProvider } from '@renderer/store/ocr'
-import { isImageOcrProvider } from '@renderer/types'
+import { isImageOcrProvider, OcrProvider } from '@renderer/types'
import { Select } from 'antd'
+import { useEffect } from 'react'
import { useTranslation } from 'react-i18next'
import { useDispatch } from 'react-redux'
import { SettingRow, SettingRowTitle } from '..'
-const logger = loggerService.withContext('OcrImageProviderSettings')
+const logger = loggerService.withContext('OcrImageSettings')
-const OcrImageProviderSettings = () => {
+type Props = {
+ setProvider: (provider: OcrProvider) => void
+}
+
+const OcrImageSettings = ({ setProvider }: Props) => {
const { t } = useTranslation()
const providers = useAppSelector((state) => state.ocr.providers)
const imageProvider = useAppSelector((state) => state.ocr.imageProvider)
const imageProviders = providers.filter((p) => isImageOcrProvider(p))
const dispatch = useDispatch()
+ // 挂载时更新外部状态
+ useEffect(() => {
+ setProvider(imageProvider)
+ }, [imageProvider, setProvider])
+
const updateImageProvider = (id: string) => {
const provider = imageProviders.find((p) => p.id === id)
if (!provider) {
@@ -25,6 +35,7 @@ const OcrImageProviderSettings = () => {
return
}
+ setProvider(provider)
dispatch(setImageOcrProvider(provider))
}
@@ -48,4 +59,4 @@ const OcrImageProviderSettings = () => {
)
}
-export default OcrImageProviderSettings
+export default OcrImageSettings
diff --git a/src/renderer/src/pages/settings/DocProcessSettings/OcrProviderSettings.tsx b/src/renderer/src/pages/settings/DocProcessSettings/OcrProviderSettings.tsx
new file mode 100644
index 0000000000..a9ba128d7a
--- /dev/null
+++ b/src/renderer/src/pages/settings/DocProcessSettings/OcrProviderSettings.tsx
@@ -0,0 +1,51 @@
+// import { loggerService } from '@logger'
+import { isBuiltinOcrProvider, OcrProvider } from '@renderer/types'
+import { getOcrProviderLogo } from '@renderer/utils/ocr'
+import { Avatar, Divider, Flex } from 'antd'
+import styled from 'styled-components'
+
+import { SettingTitle } from '..'
+import { OcrTesseractSettings } from './OcrTesseractSettings'
+
+// const logger = loggerService.withContext('OcrTesseractSettings')
+
+type Props = {
+ provider: OcrProvider
+}
+
+const OcrProviderSettings = ({ provider }: Props) => {
+ // const { t } = useTranslation()
+ const getProviderSettings = () => {
+ if (isBuiltinOcrProvider(provider)) {
+ switch (provider.id) {
+ case 'tesseract':
+ return
+ }
+ } else {
+ throw new Error('Not supported OCR provider')
+ }
+ }
+
+ return (
+ <>
+
+
+
+ {provider.name}
+
+
+
+ {getProviderSettings()}
+ >
+ )
+}
+
+const ProviderName = styled.span`
+ font-size: 14px;
+ font-weight: 500;
+`
+const ProviderLogo = styled(Avatar)`
+ border: 0.5px solid var(--color-border);
+`
+
+export default OcrProviderSettings
diff --git a/src/renderer/src/pages/settings/DocProcessSettings/OcrSettings.tsx b/src/renderer/src/pages/settings/DocProcessSettings/OcrSettings.tsx
index d8763f0e2f..9ad2d111ad 100644
--- a/src/renderer/src/pages/settings/DocProcessSettings/OcrSettings.tsx
+++ b/src/renderer/src/pages/settings/DocProcessSettings/OcrSettings.tsx
@@ -1,22 +1,27 @@
import { PictureOutlined } from '@ant-design/icons'
import { useTheme } from '@renderer/context/ThemeProvider'
+import { useAppSelector } from '@renderer/store'
+import { OcrProvider } from '@renderer/types'
import { Tabs, TabsProps } from 'antd'
-import { FC } from 'react'
+import { FC, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { SettingDivider, SettingGroup, SettingTitle } from '..'
-import OcrImageProviderSettings from './OcrImageProviderSettings'
+import OcrImageSettings from './OcrImageSettings'
+import OcrProviderSettings from './OcrProviderSettings'
const OcrSettings: FC = () => {
const { t } = useTranslation()
const { theme: themeMode } = useTheme()
+ const imageProvider = useAppSelector((state) => state.ocr.imageProvider)
+ const [provider, setProvider] = useState(imageProvider) // since default to image provider
const tabs: TabsProps['items'] = [
{
key: 'image',
label: t('settings.tool.ocr.image.title'),
icon: ,
- children:
+ children:
}
]
@@ -27,6 +32,9 @@ const OcrSettings: FC = () => {
+
+
+
>
)
}
diff --git a/src/renderer/src/pages/settings/DocProcessSettings/OcrTesseractSettings.tsx b/src/renderer/src/pages/settings/DocProcessSettings/OcrTesseractSettings.tsx
new file mode 100644
index 0000000000..7e94a31194
--- /dev/null
+++ b/src/renderer/src/pages/settings/DocProcessSettings/OcrTesseractSettings.tsx
@@ -0,0 +1,52 @@
+// import { loggerService } from '@logger'
+import InfoTooltip from '@renderer/components/InfoTooltip'
+import { useOcrProvider } from '@renderer/hooks/useOcrProvider'
+import { BuiltinOcrProviderIds, isOcrTesseractProvider } from '@renderer/types'
+import { Flex, Select } from 'antd'
+import { useTranslation } from 'react-i18next'
+
+import { SettingRow, SettingRowTitle } from '..'
+
+// const logger = loggerService.withContext('OcrTesseractSettings')
+
+export const OcrTesseractSettings = () => {
+ const { t } = useTranslation()
+ const { provider } = useOcrProvider(BuiltinOcrProviderIds.tesseract)
+
+ // TODO: use error boundary
+ if (!isOcrTesseractProvider(provider)) {
+ throw new Error('Not tesseract provider.')
+ }
+
+ // const [langs, setLangs] = useState(provider.config?.langs ?? {})
+
+ // currently static
+ const options = [
+ { value: 'chi_sim', label: t('languages.chinese') },
+ { value: 'chi_tra', label: t('languages.chinese-traditional') },
+ { value: 'eng', label: t('languages.english') }
+ ]
+
+ return (
+ <>
+
+
+
+ {t('settings.tool.ocr.image.tesseract.langs')}
+
+
+
+
+
+
+
+ >
+ )
+}
diff --git a/src/renderer/src/store/ocr.ts b/src/renderer/src/store/ocr.ts
index 87d9b790bf..7e4ba3d348 100644
--- a/src/renderer/src/store/ocr.ts
+++ b/src/renderer/src/store/ocr.ts
@@ -1,6 +1,6 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
import { BUILTIN_OCR_PROVIDERS, DEFAULT_OCR_PROVIDER } from '@renderer/config/ocr'
-import { ImageOcrProvider, OcrProvider } from '@renderer/types'
+import { ImageOcrProvider, OcrProvider, OcrProviderConfig } from '@renderer/types'
export interface OcrState {
providers: OcrProvider[]
@@ -22,8 +22,8 @@ const ocrSlice = createSlice({
addOcrProvider(state, action: PayloadAction) {
state.providers.push(action.payload)
},
- removeOcrProvider(state, action: PayloadAction) {
- state.providers = state.providers.filter((provider) => provider.id !== action.payload.id)
+ removeOcrProvider(state, action: PayloadAction) {
+ state.providers = state.providers.filter((provider) => provider.id !== action.payload)
},
updateOcrProvider(state, action: PayloadAction>) {
const index = state.providers.findIndex((provider) => provider.id === action.payload.id)
@@ -31,13 +31,31 @@ const ocrSlice = createSlice({
Object.assign(state.providers[index], action.payload)
}
},
+ updateOcrProviderConfig(
+ state,
+ action: PayloadAction<{ id: string; update: Omit, 'id'> }>
+ ) {
+ const index = state.providers.findIndex((provider) => provider.id === action.payload.id)
+ if (index !== -1) {
+ if (!state.providers[index].config) {
+ state.providers[index].config = {}
+ }
+ Object.assign(state.providers[index].config, action.payload.update)
+ }
+ },
setImageOcrProvider(state, action: PayloadAction) {
state.imageProvider = action.payload
}
}
})
-export const { setOcrProviders, addOcrProvider, removeOcrProvider, updateOcrProvider, setImageOcrProvider } =
- ocrSlice.actions
+export const {
+ setOcrProviders,
+ addOcrProvider,
+ removeOcrProvider,
+ updateOcrProvider,
+ updateOcrProviderConfig,
+ setImageOcrProvider
+} = ocrSlice.actions
export default ocrSlice.reducer
diff --git a/src/renderer/src/types/ocr.ts b/src/renderer/src/types/ocr.ts
index e443e1597f..38c4ba6c5b 100644
--- a/src/renderer/src/types/ocr.ts
+++ b/src/renderer/src/types/ocr.ts
@@ -1,3 +1,5 @@
+import Tesseract from 'tesseract.js'
+
import { FileMetadata, ImageFileMetadata, isImageFile } from '.'
export const BuiltinOcrProviderIds = {
@@ -123,3 +125,18 @@ export type OcrResult = {
export type OcrHandler = (file: SupportedOcrFile) => Promise
export type OcrImageHandler = (file: ImageFileMetadata) => Promise
+
+// Tesseract Types
+export type OcrTesseractConfig = OcrProviderConfig & {
+ langs: Record
+}
+
+export type OcrTesseractProvider = BuiltinOcrProvider & {
+ config: OcrTesseractConfig
+}
+
+export const isOcrTesseractProvider = (p: OcrProvider): p is OcrTesseractProvider => {
+ return p.id === BuiltinOcrProviderIds.tesseract
+}
+
+export type TesseractLangCode = Tesseract.LanguageCode
diff --git a/yarn.lock b/yarn.lock
index 7b9986f6c0..f57df818e7 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -8579,7 +8579,7 @@ __metadata:
string-width: "npm:^7.2.0"
styled-components: "npm:^6.1.11"
tar: "npm:^7.4.3"
- tesseract.js: "npm:^6.0.1"
+ tesseract.js: "patch:tesseract.js@npm%3A6.0.1#~/.yarn/patches/tesseract.js-npm-6.0.1-2562a7e46d.patch"
tiny-pinyin: "npm:^1.3.2"
tokenx: "npm:^1.1.0"
tsx: "npm:^4.20.3"
@@ -20978,7 +20978,7 @@ __metadata:
languageName: node
linkType: hard
-"tesseract.js@npm:*, tesseract.js@npm:^6.0.1":
+"tesseract.js@npm:6.0.1":
version: 6.0.1
resolution: "tesseract.js@npm:6.0.1"
dependencies:
@@ -20995,6 +20995,23 @@ __metadata:
languageName: node
linkType: hard
+"tesseract.js@patch:tesseract.js@npm%3A6.0.1#~/.yarn/patches/tesseract.js-npm-6.0.1-2562a7e46d.patch":
+ version: 6.0.1
+ resolution: "tesseract.js@patch:tesseract.js@npm%3A6.0.1#~/.yarn/patches/tesseract.js-npm-6.0.1-2562a7e46d.patch::version=6.0.1&hash=a9cf7b"
+ dependencies:
+ bmp-js: "npm:^0.1.0"
+ idb-keyval: "npm:^6.2.0"
+ is-url: "npm:^1.2.4"
+ node-fetch: "npm:^2.6.9"
+ opencollective-postinstall: "npm:^2.0.3"
+ regenerator-runtime: "npm:^0.13.3"
+ tesseract.js-core: "npm:^6.0.0"
+ wasm-feature-detect: "npm:^1.2.11"
+ zlibjs: "npm:^0.3.1"
+ checksum: 10c0/8a94fcc688ff21a9e82b721563d8fa174837ba807d0f01290fe9a1bb6a1c96ecaf7dc1c83510510f3d5185abd15f1cc5fc3cb7ad6c0eee0c4b3e278106f8a5da
+ languageName: node
+ linkType: hard
+
"test-exclude@npm:^7.0.1":
version: 7.0.1
resolution: "test-exclude@npm:7.0.1"