From a4489f4945578644c861863dcf6a8d014f0072b7 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 24 Jan 2026 00:16:32 +0800 Subject: [PATCH] feat: add OCR language extension configuration and documentation - Introduced `compose.ocr-languages.yml` for installing additional Tesseract OCR language packs during container startup. - Updated documentation to include new OCR language extension options and usage scenarios. - Enhanced existing documentation with references to the new OCR language extension guide. - Improved test coverage for the deark converter and formats, ensuring accurate format handling and error management. - Refactored code to streamline file system operations and improve readability. --- docs/Docker組合配置/OCR語言擴展.md | 345 ++++++++++++++++++ docs/Docker組合配置/compose.ocr-languages.yml | 96 +++++ docs/Docker組合配置/說明文件.md | 39 +- docs/功能說明/OCR.md | 59 +-- docs/功能說明/翻譯功能.md | 11 + docs/快速入門/概覽.md | 2 +- docs/範例配置/compose.ocr-languages.yml | 96 +++++ docs/範例配置/說明文件.md | 13 +- src/converters/deark.ts | 2 +- tests/converters/deark.test.ts | 61 ++-- tests/converters/formats.test.ts | 132 ++++++- 11 files changed, 766 insertions(+), 90 deletions(-) create mode 100644 docs/Docker組合配置/OCR語言擴展.md create mode 100644 docs/Docker組合配置/compose.ocr-languages.yml create mode 100644 docs/範例配置/compose.ocr-languages.yml diff --git a/docs/Docker組合配置/OCR語言擴展.md b/docs/Docker組合配置/OCR語言擴展.md new file mode 100644 index 0000000..726351e --- /dev/null +++ b/docs/Docker組合配置/OCR語言擴展.md @@ -0,0 +1,345 @@ +# OCR 語言擴展指南 + +ConvertX-CN 預設內建 7 種 OCR 語言(+ 自動多語言模式),但你可以透過多種方式擴展支援更多語言。 + +--- + +## 內建語言 + +以下語言已預先安裝,無需額外配置: + +| 語言 | Tesseract 代碼 | UI 格式代碼 | +| -------- | -------------- | ----------- | +| 英文 | `eng` | `pdf-en` | +| 繁體中文 | `chi_tra` | `pdf-zh-TW` | +| 簡體中文 | `chi_sim` | `pdf-zh` | +| 日文 | `jpn` | `pdf-ja` | +| 韓文 | `kor` | `pdf-ko` | +| 德文 | `deu` | `pdf-de` | +| 法文 | `fra` | `pdf-fr` | + +另外還有 `pdf-ocr` 自動多語言模式,會同時使用所有已安裝的語言包。 + +--- + +## 擴展方法比較 + +| 方法 | 優點 | 缺點 | 適用場景 | +| --------------------------- | ---------------------- | ------------------ | -------------- | +| **方法一:compose.yaml** | 簡單、無需建立額外檔案 | 每次啟動需重新安裝 | 測試、臨時使用 | +| **方法二:掛載語言包** | 離線可用、啟動快 | 需手動下載檔案 | 離線環境 | +| **方法三:自訂 Dockerfile** | 一次安裝、啟動最快 | 需要建立自訂 image | 生產環境 | + +--- + +## 方法一:透過 compose.yaml 安裝 + +### 完整範例 + +```yaml +# compose.yaml +services: + convertx: + image: ghcr.io/cysk003/convertx-cn:latest + container_name: convertx-cn + restart: unless-stopped + + # 使用自訂 entrypoint 安裝額外語言包 + entrypoint: ["/bin/sh", "-c"] + command: + - | + echo "📦 正在安裝額外 OCR 語言包..." + apt-get update && apt-get install -y --no-install-recommends \ + tesseract-ocr-spa \ + tesseract-ocr-ita \ + tesseract-ocr-por \ + tesseract-ocr-rus \ + && rm -rf /var/lib/apt/lists/* + echo "✅ 語言包安裝完成" + echo "🚀 啟動 ConvertX..." + exec bun run start + + ports: + - "3000:3000" + + volumes: + - ./data:/app/data + + environment: + - TZ=Asia/Taipei + - JWT_SECRET=請改成你自己的長隨機字串-至少32個字元 + - ACCOUNT_REGISTRATION=true + - HTTP_ALLOWED=true + - ALLOW_UNAUTHENTICATED=false + - AUTO_DELETE_EVERY_N_HOURS=24 +``` + +### 說明 + +1. `entrypoint` 和 `command` 覆蓋了原本的啟動命令 +2. 在啟動應用前先執行 `apt-get install` 安裝語言包 +3. `exec bun run start` 確保應用正確啟動並接收信號 + +### 注意事項 + +> ⚠️ **每次容器重啟都會重新安裝語言包**,首次啟動可能需要 1-2 分鐘。 + +> 💡 如果需要更快的啟動速度,請使用方法二或方法三。 + +--- + +## 方法二:掛載語言包(推薦離線環境) + +### 步驟 1:下載語言包 + +```bash +# 建立 tessdata 目錄 +mkdir -p tessdata +cd tessdata + +# 下載需要的語言包 +# 高精度版(推薦,檔案較大) +wget https://github.com/tesseract-ocr/tessdata_best/raw/main/spa.traineddata # 西班牙文 +wget https://github.com/tesseract-ocr/tessdata_best/raw/main/ita.traineddata # 義大利文 +wget https://github.com/tesseract-ocr/tessdata_best/raw/main/por.traineddata # 葡萄牙文 +wget https://github.com/tesseract-ocr/tessdata_best/raw/main/rus.traineddata # 俄文 + +# 或使用標準版(檔案較小,速度較快) +# wget https://github.com/tesseract-ocr/tessdata/raw/main/spa.traineddata +``` + +### 步驟 2:配置 compose.yaml + +```yaml +# compose.yaml +services: + convertx: + image: ghcr.io/cysk003/convertx-cn:latest + container_name: convertx-cn + restart: unless-stopped + + ports: + - "3000:3000" + + volumes: + - ./data:/app/data + # 掛載語言包目錄 + - ./tessdata:/usr/share/tesseract-ocr/5/tessdata:ro + + environment: + - TZ=Asia/Taipei + - JWT_SECRET=請改成你自己的長隨機字串-至少32個字元 +``` + +### 目錄結構 + +``` +your-project/ +├── compose.yaml +├── data/ +└── tessdata/ + ├── spa.traineddata + ├── ita.traineddata + ├── por.traineddata + └── rus.traineddata +``` + +### 語言包下載來源 + +| 版本 | 說明 | 下載位置 | +| -------- | -------------------- | ---------------------------------------------- | +| 高精度版 | 辨識準確度最高 | https://github.com/tesseract-ocr/tessdata_best | +| 標準版 | 平衡準確度與速度 | https://github.com/tesseract-ocr/tessdata | +| 快速版 | 速度最快,準確度較低 | https://github.com/tesseract-ocr/tessdata_fast | + +--- + +## 方法三:自訂 Dockerfile(推薦生產環境) + +### 步驟 1:建立 Dockerfile + +```dockerfile +# Dockerfile +FROM ghcr.io/cysk003/convertx-cn:latest + +# 安裝額外 OCR 語言包 +RUN apt-get update && apt-get install -y --no-install-recommends \ + tesseract-ocr-spa \ + tesseract-ocr-ita \ + tesseract-ocr-por \ + tesseract-ocr-rus \ + tesseract-ocr-ara \ + tesseract-ocr-hin \ + tesseract-ocr-vie \ + tesseract-ocr-tha \ + && rm -rf /var/lib/apt/lists/* + +# 可選:驗證安裝 +RUN tesseract --list-langs +``` + +### 步驟 2:配置 compose.yaml + +```yaml +# compose.yaml +services: + convertx: + build: + context: . + dockerfile: Dockerfile + container_name: convertx-cn + restart: unless-stopped + + ports: + - "3000:3000" + + volumes: + - ./data:/app/data + + environment: + - TZ=Asia/Taipei + - JWT_SECRET=請改成你自己的長隨機字串-至少32個字元 +``` + +### 步驟 3:建置並啟動 + +```bash +# 建置自訂 image +docker compose build + +# 啟動 +docker compose up -d +``` + +### 目錄結構 + +``` +your-project/ +├── compose.yaml +├── Dockerfile +└── data/ +``` + +--- + +## 可用語言包完整列表 + +### 常用語言 + +| Tesseract 包名 | 語言代碼 | 語言 | 檔案大小(約) | +| ------------------- | -------- | -------- | -------------- | +| `tesseract-ocr-spa` | es | 西班牙文 | 5 MB | +| `tesseract-ocr-ita` | it | 義大利文 | 4 MB | +| `tesseract-ocr-por` | pt | 葡萄牙文 | 4 MB | +| `tesseract-ocr-rus` | ru | 俄文 | 6 MB | +| `tesseract-ocr-ara` | ar | 阿拉伯文 | 3 MB | +| `tesseract-ocr-hin` | hi | 印地文 | 7 MB | +| `tesseract-ocr-vie` | vi | 越南文 | 2 MB | +| `tesseract-ocr-tha` | th | 泰文 | 5 MB | + +### 歐洲語言 + +| Tesseract 包名 | 語言代碼 | 語言 | +| ------------------- | -------- | ------------ | +| `tesseract-ocr-swe` | sv | 瑞典文 | +| `tesseract-ocr-dan` | da | 丹麥文 | +| `tesseract-ocr-nor` | no | 挪威文 | +| `tesseract-ocr-fin` | fi | 芬蘭文 | +| `tesseract-ocr-pol` | pl | 波蘭文 | +| `tesseract-ocr-ces` | cs | 捷克文 | +| `tesseract-ocr-hun` | hu | 匈牙利文 | +| `tesseract-ocr-nld` | nl | 荷蘭文 | +| `tesseract-ocr-ell` | el | 希臘文 | +| `tesseract-ocr-ukr` | uk | 烏克蘭文 | +| `tesseract-ocr-ron` | ro | 羅馬尼亞文 | +| `tesseract-ocr-bul` | bg | 保加利亞文 | +| `tesseract-ocr-hrv` | hr | 克羅地亞文 | +| `tesseract-ocr-slk` | sk | 斯洛伐克文 | +| `tesseract-ocr-slv` | sl | 斯洛維尼亞文 | + +### 中東語言 + +| Tesseract 包名 | 語言代碼 | 語言 | +| ------------------- | -------- | -------- | +| `tesseract-ocr-heb` | he | 希伯來文 | +| `tesseract-ocr-tur` | tr | 土耳其文 | +| `tesseract-ocr-fas` | fa | 波斯文 | + +### 亞洲語言 + +| Tesseract 包名 | 語言代碼 | 語言 | +| ------------------- | -------- | ------------ | +| `tesseract-ocr-ben` | bn | 孟加拉文 | +| `tesseract-ocr-tam` | ta | 泰米爾文 | +| `tesseract-ocr-tel` | te | 泰盧固文 | +| `tesseract-ocr-kan` | kn | 卡納達文 | +| `tesseract-ocr-mal` | ml | 馬拉雅拉姆文 | +| `tesseract-ocr-mar` | mr | 馬拉地文 | +| `tesseract-ocr-guj` | gu | 古吉拉特文 | +| `tesseract-ocr-pan` | pa | 旁遮普文 | +| `tesseract-ocr-mya` | my | 緬甸文 | +| `tesseract-ocr-khm` | km | 高棉文 | +| `tesseract-ocr-lao` | lo | 寮文 | +| `tesseract-ocr-ind` | id | 印尼文 | +| `tesseract-ocr-msa` | ms | 馬來文 | + +> 💡 **查詢所有可用語言包**:在容器內執行 `apt-cache search tesseract-ocr-` + +--- + +## 驗證語言包安裝 + +### 方法 1:在容器內檢查 + +```bash +# 進入容器 +docker exec -it convertx-cn /bin/bash + +# 列出已安裝的語言 +tesseract --list-langs +``` + +### 方法 2:透過日誌確認 + +啟動容器後查看日誌: + +```bash +docker compose logs -f convertx +``` + +如果使用方法一(compose.yaml 安裝),會看到類似以下輸出: + +``` +📦 正在安裝額外 OCR 語言包... +✅ 語言包安裝完成 +🚀 啟動 ConvertX... +``` + +--- + +## 常見問題 + +### Q: 為什麼 UI 上看不到新增的語言選項? + +**A:** OCRmyPDF 的 UI 格式是在程式碼中固定的(8 種選項),新增語言包只會影響 `pdf-ocr` 自動模式的辨識能力。如果你使用 `pdf-ocr` 模式,系統會自動使用所有已安裝的語言包。 + +### Q: 自動模式會變慢嗎? + +**A:** 安裝更多語言包後,`pdf-ocr` 自動模式可能會稍微變慢,因為 Tesseract 需要嘗試更多語言。如果你知道文件語言,選擇特定語言格式會更快。 + +### Q: 如何在 UI 上顯示新的語言選項? + +**A:** 需要修改 `src/converters/ocrmypdf.ts` 中的 `SUPPORTED_LANGUAGES` 陣列,並重新建置應用。這超出了簡單配置的範圍。 + +### Q: 語言包太大,如何減少下載量? + +**A:** 使用 `tessdata_fast` 版本而非 `tessdata_best`,或只安裝實際需要的語言。 + +--- + +## 相關文件 + +- [OCR 功能說明](../功能說明/OCR.md) +- [Docker Compose 說明](說明文件.md) +- [compose.ocr-languages.yml](compose.ocr-languages.yml) +- [環境變數設定](../配置設定/環境變數.md) diff --git a/docs/Docker組合配置/compose.ocr-languages.yml b/docs/Docker組合配置/compose.ocr-languages.yml new file mode 100644 index 0000000..aa6785b --- /dev/null +++ b/docs/Docker組合配置/compose.ocr-languages.yml @@ -0,0 +1,96 @@ +# ============================================================================== +# ConvertX-CN OCR 語言擴展配置 +# +# 📚 此配置示範如何在容器啟動時安裝額外的 Tesseract OCR 語言包 +# +# 🎯 適用情境: +# - 需要 OCR 辨識西班牙文、義大利文、俄文等額外語言 +# - 翻譯引擎支援 15 種語言,但 OCR 預設只內建 8 種 +# - 需要處理更多語言的掃描版 PDF +# +# ⚠️ 注意事項: +# - 每次容器啟動時都會安裝語言包,首次啟動較慢 +# - 語言包會存在容器內,重啟後需重新安裝 +# - 若需永久安裝,請使用自訂 Dockerfile 方式 +# +# ============================================================================== + +services: + convertx: + image: convertx/convertx-cn:latest + container_name: convertx-cn + restart: unless-stopped + + # ========================================================================= + # 使用自訂 entrypoint 安裝額外語言包 + # ========================================================================= + entrypoint: ["/bin/sh", "-c"] + command: + - | + echo "📦 正在安裝額外 OCR 語言包..." + apt-get update && apt-get install -y --no-install-recommends \ + tesseract-ocr-spa \ + tesseract-ocr-ita \ + tesseract-ocr-por \ + tesseract-ocr-rus \ + && rm -rf /var/lib/apt/lists/* + echo "✅ 語言包安裝完成" + echo "🚀 啟動 ConvertX..." + exec bun run start + + ports: + - "3000:3000" + + volumes: + - ./data:/app/data + + environment: + - TZ=Asia/Taipei + - JWT_SECRET=請改成你自己的長隨機字串-至少32個字元 + - ACCOUNT_REGISTRATION=true + - HTTP_ALLOWED=true + - ALLOW_UNAUTHENTICATED=false + - AUTO_DELETE_EVERY_N_HOURS=24 + +# ============================================================================== +# 可用語言包對照表 +# ============================================================================== +# +# 💡 只需安裝你需要的語言,不必全部安裝 +# +# 語言包名稱 | 語言代碼 | 語言 +# ----------------------|---------|---------------- +# tesseract-ocr-spa | es | 西班牙文 +# tesseract-ocr-ita | it | 義大利文 +# tesseract-ocr-por | pt | 葡萄牙文 +# tesseract-ocr-rus | ru | 俄文 +# tesseract-ocr-ara | ar | 阿拉伯文 +# tesseract-ocr-hin | hi | 印地文 +# tesseract-ocr-vie | vi | 越南文 +# tesseract-ocr-tha | th | 泰文 +# tesseract-ocr-swe | sv | 瑞典文 +# tesseract-ocr-dan | da | 丹麥文 +# tesseract-ocr-nor | no | 挪威文 +# tesseract-ocr-fin | fi | 芬蘭文 +# tesseract-ocr-pol | pl | 波蘭文 +# tesseract-ocr-ces | cs | 捷克文 +# tesseract-ocr-hun | hu | 匈牙利文 +# tesseract-ocr-tur | tr | 土耳其文 +# tesseract-ocr-heb | he | 希伯來文 +# tesseract-ocr-ben | bn | 孟加拉文 +# tesseract-ocr-tam | ta | 泰米爾文 +# tesseract-ocr-ind | id | 印尼文 +# +# ============================================================================== +# 內建語言(無需額外安裝) +# ============================================================================== +# +# tesseract-ocr-eng | en | 英文 +# tesseract-ocr-chi-tra | zh-TW | 繁體中文 +# tesseract-ocr-chi-sim | zh | 簡體中文 +# tesseract-ocr-jpn | ja | 日文 +# tesseract-ocr-kor | ko | 韓文 +# tesseract-ocr-deu | de | 德文 +# tesseract-ocr-fra | fr | 法文 +# +# ============================================================================== diff --git a/docs/Docker組合配置/說明文件.md b/docs/Docker組合配置/說明文件.md index 83edfd1..d74d975 100644 --- a/docs/Docker組合配置/說明文件.md +++ b/docs/Docker組合配置/說明文件.md @@ -4,20 +4,23 @@ ## 範例檔案 -| 檔案 | 適用情境 | 說明 | -| ------------------------------------------------ | ----------- | --------------------- | -| [compose.minimal.yml](compose.minimal.yml) | Docker 老手 | 最精簡的可用配置 | -| [compose.production.yml](compose.production.yml) | 生產環境 | 含 Reverse Proxy 設定 | -| [compose.reference.yml](compose.reference.yml) | 參考文件 | 所有設定的完整參考 | +| 檔案 | 適用情境 | 說明 | +| ------------------------------------------------------ | ----------- | --------------------- | +| [compose.minimal.yml](compose.minimal.yml) | Docker 老手 | 最精簡的可用配置 | +| [compose.production.yml](compose.production.yml) | 生產環境 | 含 Reverse Proxy 設定 | +| [compose.reference.yml](compose.reference.yml) | 參考文件 | 所有設定的完整參考 | +| [compose.ocr-languages.yml](compose.ocr-languages.yml) | OCR 擴展 | 安裝額外 OCR 語言包 | +| [OCR語言擴展.md](OCR語言擴展.md) | 詳細指南 | OCR 語言擴展完整說明 | ## 快速選擇 -| 你是... | 使用 | -| ------------ | ----------------------------- | -| 新手 | [README 主頁](../說明文件.md) | -| Docker 熟手 | compose.minimal.yml | -| 生產環境 | compose.production.yml | -| 查詢所有選項 | compose.reference.yml | +| 你是... | 使用 | +| ----------------- | -------------------------------------------- | +| 新手 | [README 主頁](../說明文件.md) | +| Docker 熟手 | compose.minimal.yml | +| 生產環境 | compose.production.yml | +| 查詢所有選項 | compose.reference.yml | +| 需要更多 OCR 語言 | [OCR語言擴展.md](OCR語言擴展.md)(詳細指南) | ## 如何使用 @@ -55,3 +58,17 @@ docker compose up -d ### 我想了解所有設定 參考 [compose.reference.yml](compose.reference.yml),包含所有環境變數的說明。 + +### 我需要更多 OCR 語言支援 + +參考 [OCR語言擴展.md](OCR語言擴展.md),這是完整的 OCR 語言擴展指南,包含: + +- 三種擴展方法的詳細說明與比較 +- 完整的 compose.yaml 範例配置 +- 50+ 種可用語言包列表 +- 語言包下載與驗證方法 +- 常見問題解答 + +> 💡 內建 OCR 語言:英文、繁體中文、簡體中文、日文、韓文、德文、法文 +> +> 翻譯引擎支援 15 種語言,但 OCR 預設只內建 8 種 diff --git a/docs/功能說明/OCR.md b/docs/功能說明/OCR.md index a770e7f..b3a4757 100644 --- a/docs/功能說明/OCR.md +++ b/docs/功能說明/OCR.md @@ -131,41 +131,47 @@ eng + chi_tra + chi_sim + jpn + kor + deu + fra ## 新增語言 -### 方法一:自訂 Dockerfile +ConvertX-CN 預設內建 7 種 OCR 語言,但你可以透過 Docker Compose 配置擴展支援更多語言。 -> 💡 在 `Dockerfile.full` 中取消註解以下內容 +### 擴展方法 -```dockerfile -RUN apt-get update && apt-get install -y --no-install-recommends \ - tesseract-ocr-spa \ - tesseract-ocr-ita \ - && rm -rf /var/lib/apt/lists/* -``` +| 方法 | 說明 | 適用場景 | +| ----------------- | -------------------------- | -------------- | +| compose.yaml 安裝 | 容器啟動時自動安裝語言包 | 測試、臨時使用 | +| 掛載語言包 | 下載 .traineddata 檔案掛載 | 離線環境 | +| 自訂 Dockerfile | 建置包含語言包的自訂映像 | 生產環境 | -- `tesseract-ocr-spa` — 西班牙文 -- `tesseract-ocr-ita` — 義大利文 +### 常用可選語言 -### 方法二:掛載語言包 +| Tesseract 包名 | 語言 | +| ------------------- | -------- | +| `tesseract-ocr-spa` | 西班牙文 | +| `tesseract-ocr-ita` | 義大利文 | +| `tesseract-ocr-por` | 葡萄牙文 | +| `tesseract-ocr-rus` | 俄文 | +| `tesseract-ocr-ara` | 阿拉伯文 | +| `tesseract-ocr-vie` | 越南文 | +| `tesseract-ocr-tha` | 泰文 | -```yaml -volumes: - - ./tessdata:/usr/share/tesseract-ocr/5/tessdata -``` - -下載語言包:https://github.com/tesseract-ocr/tessdata_best +> 📖 **詳細配置指南**:[OCR 語言擴展指南](../Docker組合配置/OCR語言擴展.md) +> +> 包含完整的 compose.yaml 範例、語言包下載方法、以及 50+ 種可用語言列表。 --- -## 可選語言包 +## OCRmyPDF vs 翻譯引擎語言支援 -| 區域 | 語言 | -| ------ | ------------------------------ | -| 西歐 | 西班牙文、義大利文、葡萄牙文 | -| 北歐 | 瑞典文、丹麥文、挪威文、芬蘭文 | -| 東歐 | 俄文、波蘭文、捷克文、匈牙利文 | -| 中東 | 阿拉伯文、希伯來文、土耳其文 | -| 南亞 | 印地文、孟加拉文、泰米爾文 | -| 東南亞 | 泰文、越南文、印尼文 | +| 功能 | OCRmyPDF | PDFMathTranslate / BabelDOC | +| -------- | --------------------------- | --------------------------- | +| 內建語言 | 8 種 | 15 種 | +| 可擴展 | ✅ 可透過 compose.yaml 添加 | ❌ 固定 | +| 語言用途 | OCR 文字辨識 | 翻譯目標語言 | + +**OCRmyPDF 內建語言**:英文、繁體中文、簡體中文、日文、韓文、德文、法文 + 自動多語言模式 + +**翻譯引擎內建語言**:英文、簡體中文、繁體中文、日文、韓文、德文、法文、西班牙文、義大利文、葡萄牙文、俄文、阿拉伯文、印地文、越南文、泰文 + +> 💡 如果需要 OCR 更多語言,請參考 [OCR 語言擴展指南](../Docker組合配置/OCR語言擴展.md)。 --- @@ -173,4 +179,5 @@ volumes: - [支援的轉換器](轉換器.md) - [翻譯功能](翻譯功能.md) +- [OCR 語言擴展指南](../Docker組合配置/OCR語言擴展.md) - [Docker 部署](../部署指南/Docker部署.md) diff --git a/docs/功能說明/翻譯功能.md b/docs/功能說明/翻譯功能.md index 77ecd5b..582d87c 100644 --- a/docs/功能說明/翻譯功能.md +++ b/docs/功能說明/翻譯功能.md @@ -75,6 +75,17 @@ ConvertX-CN 內建兩個 PDF 翻譯引擎: > 💡 BabelDOC 額外支援 `md-` 和 `html-` 格式 +### 與 OCRmyPDF 語言差異 + +| 功能 | 翻譯引擎 | OCRmyPDF | +| -------- | ------------ | -------------- | +| 支援語言 | 15 種 | 8 種(可擴展) | +| 用途 | 翻譯目標語言 | OCR 文字辨識 | + +翻譯引擎支援 15 種目標語言,而 OCRmyPDF 預設只內建 8 種 OCR 語言。 + +如需擴展 OCRmyPDF 語言支援,請參考 [OCR 功能 - 新增語言](OCR.md#新增語言)。 + --- ## 輸出格式 diff --git a/docs/快速入門/概覽.md b/docs/快速入門/概覽.md index 6f3af8d..60f70fe 100644 --- a/docs/快速入門/概覽.md +++ b/docs/快速入門/概覽.md @@ -71,7 +71,7 @@ ConvertX-CN 是 fork 自 [C4illin/ConvertX](https://github.com/C4illin/ConvertX) | CJK 字型 | ❌ | ✅ Noto CJK + 標楷體 | | TexLive | 基本 | 完整 | | 繁中介面 | ❌ | ✅ 預設 | -| MinerU | ❌ | ✅ 包含 | +| MinerU | ❌ | ✅ 包含 | | PDF 翻譯 | ❌ | ✅ PDFMathTranslate | --- diff --git a/docs/範例配置/compose.ocr-languages.yml b/docs/範例配置/compose.ocr-languages.yml new file mode 100644 index 0000000..aa6785b --- /dev/null +++ b/docs/範例配置/compose.ocr-languages.yml @@ -0,0 +1,96 @@ +# ============================================================================== +# ConvertX-CN OCR 語言擴展配置 +# +# 📚 此配置示範如何在容器啟動時安裝額外的 Tesseract OCR 語言包 +# +# 🎯 適用情境: +# - 需要 OCR 辨識西班牙文、義大利文、俄文等額外語言 +# - 翻譯引擎支援 15 種語言,但 OCR 預設只內建 8 種 +# - 需要處理更多語言的掃描版 PDF +# +# ⚠️ 注意事項: +# - 每次容器啟動時都會安裝語言包,首次啟動較慢 +# - 語言包會存在容器內,重啟後需重新安裝 +# - 若需永久安裝,請使用自訂 Dockerfile 方式 +# +# ============================================================================== + +services: + convertx: + image: convertx/convertx-cn:latest + container_name: convertx-cn + restart: unless-stopped + + # ========================================================================= + # 使用自訂 entrypoint 安裝額外語言包 + # ========================================================================= + entrypoint: ["/bin/sh", "-c"] + command: + - | + echo "📦 正在安裝額外 OCR 語言包..." + apt-get update && apt-get install -y --no-install-recommends \ + tesseract-ocr-spa \ + tesseract-ocr-ita \ + tesseract-ocr-por \ + tesseract-ocr-rus \ + && rm -rf /var/lib/apt/lists/* + echo "✅ 語言包安裝完成" + echo "🚀 啟動 ConvertX..." + exec bun run start + + ports: + - "3000:3000" + + volumes: + - ./data:/app/data + + environment: + - TZ=Asia/Taipei + - JWT_SECRET=請改成你自己的長隨機字串-至少32個字元 + - ACCOUNT_REGISTRATION=true + - HTTP_ALLOWED=true + - ALLOW_UNAUTHENTICATED=false + - AUTO_DELETE_EVERY_N_HOURS=24 + +# ============================================================================== +# 可用語言包對照表 +# ============================================================================== +# +# 💡 只需安裝你需要的語言,不必全部安裝 +# +# 語言包名稱 | 語言代碼 | 語言 +# ----------------------|---------|---------------- +# tesseract-ocr-spa | es | 西班牙文 +# tesseract-ocr-ita | it | 義大利文 +# tesseract-ocr-por | pt | 葡萄牙文 +# tesseract-ocr-rus | ru | 俄文 +# tesseract-ocr-ara | ar | 阿拉伯文 +# tesseract-ocr-hin | hi | 印地文 +# tesseract-ocr-vie | vi | 越南文 +# tesseract-ocr-tha | th | 泰文 +# tesseract-ocr-swe | sv | 瑞典文 +# tesseract-ocr-dan | da | 丹麥文 +# tesseract-ocr-nor | no | 挪威文 +# tesseract-ocr-fin | fi | 芬蘭文 +# tesseract-ocr-pol | pl | 波蘭文 +# tesseract-ocr-ces | cs | 捷克文 +# tesseract-ocr-hun | hu | 匈牙利文 +# tesseract-ocr-tur | tr | 土耳其文 +# tesseract-ocr-heb | he | 希伯來文 +# tesseract-ocr-ben | bn | 孟加拉文 +# tesseract-ocr-tam | ta | 泰米爾文 +# tesseract-ocr-ind | id | 印尼文 +# +# ============================================================================== +# 內建語言(無需額外安裝) +# ============================================================================== +# +# tesseract-ocr-eng | en | 英文 +# tesseract-ocr-chi-tra | zh-TW | 繁體中文 +# tesseract-ocr-chi-sim | zh | 簡體中文 +# tesseract-ocr-jpn | ja | 日文 +# tesseract-ocr-kor | ko | 韓文 +# tesseract-ocr-deu | de | 德文 +# tesseract-ocr-fra | fr | 法文 +# +# ============================================================================== diff --git a/docs/範例配置/說明文件.md b/docs/範例配置/說明文件.md index fcda7cf..12afebb 100644 --- a/docs/範例配置/說明文件.md +++ b/docs/範例配置/說明文件.md @@ -6,12 +6,13 @@ ## 範例檔案 -| 檔案 | 用途 | 說明 | -| -------------------------- | ---------- | ---------------- | -| `compose.minimal.yml` | 快速啟動 | 最精簡配置 | -| `compose.production.yml` | 生產環境 | 包含安全設定 | -| `compose.with-traefik.yml` | 反向代理 | Traefik 整合 | -| `nginx.example.conf` | Nginx 設定 | 反向代理設定範例 | +| 檔案 | 用途 | 說明 | +| --------------------------- | ------------ | ----------------- | +| `最小配置.yml` | 快速啟動 | 最精簡配置 | +| `生產環境配置.yml` | 生產環境 | 包含安全設定 | +| `compose.ocr-languages.yml` | OCR 語言擴展 | 安裝額外 OCR 語言 | +| `Traefik配置.yml` | 反向代理 | Traefik 整合 | +| `Nginx範例配置.conf` | Nginx 設定 | 反向代理設定範例 | --- diff --git a/src/converters/deark.ts b/src/converters/deark.ts index 03227ad..de87cd1 100644 --- a/src/converters/deark.ts +++ b/src/converters/deark.ts @@ -1,5 +1,5 @@ import { execFile as execFileOriginal } from "node:child_process"; -import { mkdirSync, existsSync, readdirSync, unlinkSync, rmdirSync, statSync } from "node:fs"; +import { mkdirSync, existsSync, readdirSync, unlinkSync, rmdirSync } from "node:fs"; import { join, basename, dirname } from "node:path"; import { ExecFileFn } from "./types"; diff --git a/tests/converters/deark.test.ts b/tests/converters/deark.test.ts index 8bfa57b..146c7cd 100644 --- a/tests/converters/deark.test.ts +++ b/tests/converters/deark.test.ts @@ -1,5 +1,6 @@ import { test, expect, describe, mock } from "bun:test"; import { convert, properties } from "../../src/converters/deark"; +import type { ExecFileFn } from "../../src/converters/types"; describe("deark converter", () => { test("properties should have correct structure", () => { @@ -9,32 +10,34 @@ describe("deark converter", () => { expect(properties.outputMode).toBe("archive"); }); - test("properties.from should contain archive formats", () => { - expect(properties.from.archive).toBeDefined(); - expect(properties.from.archive).toContain("zip"); - expect(properties.from.archive).toContain("lha"); - expect(properties.from.archive).toContain("arc"); + test("properties.from should contain supported formats", () => { + expect(properties.from.files).toBeDefined(); + // Archive formats + expect(properties.from.files).toContain("zip"); + expect(properties.from.files).toContain("lha"); + expect(properties.from.files).toContain("arc"); + // Image formats + expect(properties.from.files).toContain("ico"); + expect(properties.from.files).toContain("bmp"); + expect(properties.from.files).toContain("pcx"); }); - test("properties.from should contain image formats", () => { - expect(properties.from.images).toBeDefined(); - expect(properties.from.images).toContain("ico"); - expect(properties.from.images).toContain("bmp"); - expect(properties.from.images).toContain("pcx"); - }); - - test("properties.to should have extract option for all categories", () => { - for (const category of Object.keys(properties.from)) { - expect(properties.to[category]).toBeDefined(); - expect(properties.to[category]).toContain("extract"); - } + test("properties.to should have extract option", () => { + expect(properties.to.files).toBeDefined(); + expect(properties.to.files).toContain("extract"); }); test("convert resolves when execFile succeeds", async () => { - const mockExecFile = mock((cmd: string, args: string[], callback: Function) => { - // First call is deark, second call is tar - callback(null, "Success", ""); - }); + const mockExecFile = mock( + ( + cmd: string, + args: string[], + callback: (error: Error | null, stdout: string, stderr: string) => void, + ) => { + // First call is deark, second call is tar + callback(null, "Success", ""); + }, + ); // Mock fs functions would be needed for full test // This is a simplified test @@ -45,7 +48,7 @@ describe("deark converter", () => { "extract", "/tmp/output/test", {}, - mockExecFile as any, + mockExecFile as ExecFileFn, ); } catch (error) { // Expected to fail due to fs operations not being mocked @@ -54,9 +57,15 @@ describe("deark converter", () => { }); test("convert rejects when deark fails", async () => { - const mockExecFile = mock((cmd: string, args: string[], callback: Function) => { - callback(new Error("deark failed"), "", "Unknown or unsupported format"); - }); + const mockExecFile = mock( + ( + cmd: string, + args: string[], + callback: (error: Error | null, stdout: string, stderr: string) => void, + ) => { + callback(new Error("deark failed"), "", "Unknown or unsupported format"); + }, + ); try { await convert( @@ -65,7 +74,7 @@ describe("deark converter", () => { "extract", "/tmp/output/test", {}, - mockExecFile as any, + mockExecFile as ExecFileFn, ); expect(true).toBe(false); // Should not reach here } catch (error) { diff --git a/tests/converters/formats.test.ts b/tests/converters/formats.test.ts index 00b6dd5..dd26658 100644 --- a/tests/converters/formats.test.ts +++ b/tests/converters/formats.test.ts @@ -41,7 +41,25 @@ describe("FFmpeg 格式", () => { const toFormats = ffmpegProps.to.muxer; // 關鍵影片格式(wmv 由 asf 處理) - const keyVideoFormats = ["264", "265", "3g2", "3gp", "asf", "av1", "avi", "flv", "h264", "h265", "hevc", "mkv", "mov", "mp4", "mpeg", "mpg", "webm"]; + const keyVideoFormats = [ + "264", + "265", + "3g2", + "3gp", + "asf", + "av1", + "avi", + "flv", + "h264", + "h265", + "hevc", + "mkv", + "mov", + "mp4", + "mpeg", + "mpg", + "webm", + ]; // 關鍵音訊格式 const keyAudioFormats = ["aac", "ac3", "aiff", "flac", "m4a", "mp3", "ogg", "opus", "wav", "wma"]; // 關鍵字幕格式 @@ -110,7 +128,21 @@ describe("ImageMagick 格式", () => { // 關鍵 RAW 相機格式 const keyRawFormats = ["arw", "cr2", "cr3", "dng", "nef", "orf", "raf"]; // 關鍵常見圖片格式 - const keyImageFormats = ["apng", "avif", "bmp", "gif", "heic", "heif", "ico", "jpeg", "jpg", "jxl", "png", "tiff", "webp"]; + const keyImageFormats = [ + "apng", + "avif", + "bmp", + "gif", + "heic", + "heif", + "ico", + "jpeg", + "jpg", + "jxl", + "png", + "tiff", + "webp", + ]; // 關鍵向量格式 const keyVectorFormats = ["eps", "pdf", "ps", "psd", "svg"]; @@ -913,8 +945,16 @@ describe("PDFMathTranslate 格式", () => { const toFormats = pdfmathtranslateProps.to.document; const keyOutputFormats = [ - "pdf-en", "pdf-zh", "pdf-zh-TW", "pdf-ja", "pdf-ko", - "pdf-de", "pdf-fr", "pdf-es", "pdf-it", "pdf-ru" + "pdf-en", + "pdf-zh", + "pdf-zh-TW", + "pdf-ja", + "pdf-ko", + "pdf-de", + "pdf-fr", + "pdf-es", + "pdf-it", + "pdf-ru", ]; test("輸入格式應為 pdf", () => { @@ -1011,7 +1051,13 @@ describe("OCRmyPDF 格式", () => { const toFormats = ocrmypdfProps.to.document; const keyOutputFormats = [ - "pdf-en", "pdf-zh-TW", "pdf-zh", "pdf-ja", "pdf-ko", "pdf-de", "pdf-fr" + "pdf-en", + "pdf-zh-TW", + "pdf-zh", + "pdf-ja", + "pdf-ko", + "pdf-de", + "pdf-fr", ]; test("輸入格式應為 pdf", () => { @@ -1029,8 +1075,8 @@ describe("OCRmyPDF 格式", () => { console.log(`OCRmyPDF 輸入格式:${fromFormats.length} 種`); }); - test("輸出格式數量應等於 7 種", () => { - expect(toFormats.length).toBe(7); + test("輸出格式數量應等於 8 種", () => { + expect(toFormats.length).toBe(8); console.log(`OCRmyPDF 輸出格式:${toFormats.length} 種`); }); @@ -1081,14 +1127,38 @@ describe("格式總覽", () => { const summary = [ { name: "FFmpeg", from: ffmpegProps.from.muxer.length, to: ffmpegProps.to.muxer.length }, - { name: "ImageMagick", from: imagemagickProps.from.images.length, to: imagemagickProps.to.images.length }, - { name: "GraphicsMagick", from: graphicsmagickProps.from.image.length, to: graphicsmagickProps.to.image.length }, + { + name: "ImageMagick", + from: imagemagickProps.from.images.length, + to: imagemagickProps.to.images.length, + }, + { + name: "GraphicsMagick", + from: graphicsmagickProps.from.image.length, + to: graphicsmagickProps.to.image.length, + }, { name: "Vips", from: vipsProps.from.images.length, to: vipsProps.to.images.length }, - { name: "LibreOffice", from: libreofficeProps.from.text.length, to: libreofficeProps.to.text.length }, + { + name: "LibreOffice", + from: libreofficeProps.from.text.length, + to: libreofficeProps.to.text.length, + }, { name: "Pandoc", from: pandocProps.from.text.length, to: pandocProps.to.text.length }, - { name: "Calibre", from: calibreProps.from.document.length, to: calibreProps.to.document.length }, - { name: "Inkscape", from: inkscapeProps.from.images.length, to: inkscapeProps.to.images.length }, - { name: "libjxl", from: libjxlProps.from.jxl.length + libjxlProps.from.images.length, to: libjxlProps.to.jxl.length + libjxlProps.to.images.length }, + { + name: "Calibre", + from: calibreProps.from.document.length, + to: calibreProps.to.document.length, + }, + { + name: "Inkscape", + from: inkscapeProps.from.images.length, + to: inkscapeProps.to.images.length, + }, + { + name: "libjxl", + from: libjxlProps.from.jxl.length + libjxlProps.from.images.length, + to: libjxlProps.to.jxl.length + libjxlProps.to.images.length, + }, { name: "libheif", from: libheifProps.from.images.length, to: libheifProps.to.images.length }, { name: "Assimp", from: assimpProps.from.object.length, to: assimpProps.to.object.length }, { name: "Potrace", from: potraceProps.from.images.length, to: potraceProps.to.images.length }, @@ -1097,13 +1167,37 @@ describe("格式總覽", () => { { name: "XeLaTeX", from: xelatexProps.from.text.length, to: xelatexProps.to.text.length }, { name: "dvisvgm", from: dvisvgmProps.from.images.length, to: dvisvgmProps.to.images.length }, { name: "Dasel", from: daselProps.from.document.length, to: daselProps.to.document.length }, - { name: "msgconvert", from: msgconvertProps.from.email.length, to: msgconvertProps.to.email.length }, + { + name: "msgconvert", + from: msgconvertProps.from.email.length, + to: msgconvertProps.to.email.length, + }, { name: "VCF", from: vcfProps.from.contacts.length, to: vcfProps.to.contacts.length }, - { name: "Markitdown", from: markitdownProps.from.document.length, to: markitdownProps.to.document.length }, - { name: "MinerU", from: mineruProps.from.document.length, to: mineruProps.to.document.length }, - { name: "PDFMathTranslate", from: pdfmathtranslateProps.from.document.length, to: pdfmathtranslateProps.to.document.length }, - { name: "BabelDOC", from: babeldocProps.from.document.length, to: babeldocProps.to.document.length }, - { name: "OCRmyPDF", from: ocrmypdfProps.from.document.length, to: ocrmypdfProps.to.document.length }, + { + name: "Markitdown", + from: markitdownProps.from.document.length, + to: markitdownProps.to.document.length, + }, + { + name: "MinerU", + from: mineruProps.from.document.length, + to: mineruProps.to.document.length, + }, + { + name: "PDFMathTranslate", + from: pdfmathtranslateProps.from.document.length, + to: pdfmathtranslateProps.to.document.length, + }, + { + name: "BabelDOC", + from: babeldocProps.from.document.length, + to: babeldocProps.to.document.length, + }, + { + name: "OCRmyPDF", + from: ocrmypdfProps.from.document.length, + to: ocrmypdfProps.to.document.length, + }, ]; let totalFrom = 0;