Add comprehensive format integrity tests for various converters

This commit is contained in:
Your Name 2026-01-23 23:15:36 +08:00
parent 47968f6c80
commit 80ab233d15
8 changed files with 1384 additions and 192 deletions

View file

@ -6,7 +6,8 @@
[![Docker Pulls](https://img.shields.io/docker/pulls/convertx/convertx-cn?style=flat&logo=docker)](https://hub.docker.com/r/convertx/convertx-cn) [![Docker Pulls](https://img.shields.io/docker/pulls/convertx/convertx-cn?style=flat&logo=docker)](https://hub.docker.com/r/convertx/convertx-cn)
[![GitHub Release](https://img.shields.io/github/v/release/pi-docket/ConvertX-CN)](https://github.com/pi-docket/ConvertX-CN/releases) [![GitHub Release](https://img.shields.io/github/v/release/pi-docket/ConvertX-CN)](https://github.com/pi-docket/ConvertX-CN/releases)
[![License AGPL-3.0](https://img.shields.io/badge/license-AGPL--3.0-blue)](LICENSE) ![License AGPL-3.0](https://img.shields.io/badge/license-AGPL--3.0-blue)
![Source Available](https://img.shields.io/badge/source-available-green)
--- ---
@ -25,14 +26,14 @@
## 📚 文件 ## 📚 文件
完整文件請參閱 **[文件中心](docs/說明文件.md)** 完整文件請參閱 **[文件中心](docs/README.md)**
| 分類 | 連結 | | 分類 | 連結 |
| ----------- | -------------------------------------------------------------------------------------------------------- | | ----------- | -------------------------------------------------------------------------------------------------------- |
| 🚀 快速入門 | [概覽](docs/快速入門/概覽.md) · [快速開始](docs/快速入門/快速開始.md) · [FAQ](docs/快速入門/常見問題.md) | | 🚀 快速入門 | [概覽](docs/快速入門/概覽.md) · [快速開始](docs/快速入門/快速開始.md) · [FAQ](docs/快速入門/常見問題.md) |
| 🐳 部署指南 | [Docker](docs/部署指南/Docker.md) · [反向代理](docs/部署指南/反向代理.md) | | 🐳 部署指南 | [Docker](docs/部署指南/Docker.md) · [反向代理](docs/部署指南/反向代理.md) |
| ⚙️ 配置設定 | [環境變數](docs/配置設定/環境變數.md) · [安全性](docs/配置設定/安全性.md) | | ⚙️ 配置設定 | [環境變數](docs/配置設定/環境變數.md) · [安全性](docs/配置設定/安全性.md) |
| 🔌 功能說明 | [轉換器](docs/功能說明/轉換器.md) · [OCR](docs/功能說明/OCR.md) · [翻譯](docs/功能說明/翻譯功能.md) | | 🔌 功能說明 | [轉換器](docs/功能說明/轉換器.md) · [OCR](docs/功能說明/OCR.md) · [翻譯](docs/功能說明/翻譯.md) |
| 🔗 API | [API 總覽](docs/API/總覽.md) · [端點說明](docs/API/端點.md) | | 🔗 API | [API 總覽](docs/API/總覽.md) · [端點說明](docs/API/端點.md) |
| 👩‍💻 開發 | [專案結構](docs/開發指南/專案結構.md) · [貢獻指南](docs/開發指南/貢獻指南.md) | | 👩‍💻 開發 | [專案結構](docs/開發指南/專案結構.md) · [貢獻指南](docs/開發指南/貢獻指南.md) |
@ -148,8 +149,23 @@ docker compose up -d
--- ---
## 📄 License ## 📄 License Overview
This project is licensed under the **[GNU Affero General Public License v3.0 (AGPL-3.0)](LICENSE)**. **This is a Mixed License / Source-Available Project.**
Based on [C4illin/ConvertX](https://github.com/C4illin/ConvertX). ### 1. Upstream Components
Core components derived from [C4illin/ConvertX](https://github.com/C4illin/ConvertX) are licensed under **[GNU AGPL v3.0](LICENSE)**.
- Any modifications to these files are open source under AGPL-3.0.
### 2. Author Original Components
Original modules, UI, i18n, and new features created by the ConvertX-CN author are licensed under **[Custom Non-Commercial License](LICENSE-AUTHOR)**.
- **Commercial Use / SaaS**: ❌ Prohibited without written permission.
- **Personal / Research Use**: ✅ Allowed.
> ⚠️ **Commercial Usage**: If you plan to use this project in a commercial product, SaaS, or revenue-generating service, you **must contact the author** for a license exception regarding the custom components. The AGPL obligations (sharing source code) still apply to the upstream portions.
Check [LICENSE-OVERVIEW.md](LICENSE-OVERVIEW.md) for full details.

View file

@ -32,25 +32,44 @@ ConvertX-CN 完整版內建以下 OCR 語言:
1. 上傳 PDF 檔案 1. 上傳 PDF 檔案
2. 選擇 Converter: `OCRmyPDF` 2. 選擇 Converter: `OCRmyPDF`
3. 選擇目標語言: 3. 選擇目標格式:
| 格式 | 說明 |
| ---- | ---- | | 格式 | 說明 | 推薦場景 |
| `pdf-en` | English | | ----------- | ------------------------------ | ---------------- |
| `pdf-zh-TW` | 繁體中文 | | `pdf-ocr` | **自動檢測(多語言)** ⭐ 推薦 | 通用、多語言文件 |
| `pdf-zh` | 簡體中文 | | `pdf-en` | 僅英文 | 純英文文件 |
| `pdf-ja` | 日本語 | | `pdf-zh-TW` | 僅繁體中文 | 純繁體中文文件 |
| `pdf-ko` | 한국어 | | `pdf-zh` | 僅簡體中文 | 純簡體中文文件 |
| `pdf-de` | Deutsch | | `pdf-ja` | 僅日文 | 純日文文件 |
| `pdf-fr` | Français | | `pdf-ko` | 僅韓文 | 純韓文文件 |
| `pdf-de` | 僅德文 | 純德文文件 |
| `pdf-fr` | 僅法文 | 純法文文件 |
4. 進行轉換 4. 進行轉換
> 💡 **功能特點** > 💡 **功能特點**
> >
> - **自動多語言檢測**`pdf-ocr` 模式可處理中英混排、多語言文件
> - 自動偵測頁面方向並旋轉 > - 自動偵測頁面方向並旋轉
> - 自動校正傾斜 > - 自動校正傾斜
> - 跳過已有文字層的頁面 > - 跳過已有文字層的頁面
> - 詳細的處理進度輸出 > - 詳細的處理進度輸出
### 🆕 自動多語言檢測模式
選擇 `pdf-ocr` 時,系統會使用所有已安裝的語言包進行辨識:
```
eng + chi_tra + chi_sim + jpn + kor + deu + fra
```
**適用場景:**
- ✅ 中英混排文件(如學術論文)
- ✅ 日英混排文件
- ✅ 不確定文件語言時
- ✅ 包含多種語言的文件
### 圖片 → 文字 ### 圖片 → 文字
1. 上傳圖片PNG, JPG, TIFF 等) 1. 上傳圖片PNG, JPG, TIFF 等)
@ -96,12 +115,17 @@ ConvertX-CN 完整版內建以下 OCR 語言:
### 處理多語言文件 ### 處理多語言文件
Tesseract 可同時辨識多種語言,但準確度可能下降。 #### 方法一:使用自動檢測模式(推薦)
建議: 選擇 `pdf-ocr` 格式,系統會自動使用所有語言包進行辨識,無需手動指定語言。
- 單一語言文件使用單一語言包 - ✅ 無需猜測文件語言
- 中英混合使用 `chi_tra+eng` - ✅ 支援中英混排、多語言文件
- ✅ 一鍵處理所有情況
#### 方法二:指定單一語言
如果確定文件只包含單一語言,可選擇對應的語言格式(如 `pdf-en`),可能獲得更高的辨識準確度。
--- ---

View file

@ -31,7 +31,7 @@ ConvertX-CN 完整版已內建 24 種轉換器,支援 1000+ 種格式。
| MinerU | PDF → MD | 7 | 2 | | MinerU | PDF → MD | 7 | 2 |
| PDFMathTranslate | PDF 翻譯 | 1 | 15 | | PDFMathTranslate | PDF 翻譯 | 1 | 15 |
| BabelDOC | PDF 翻譯 | 1 | 45 | | BabelDOC | PDF 翻譯 | 1 | 45 |
| OCRmyPDF | PDF OCR | 1 | 7 | | OCRmyPDF | PDF OCR | 1 | 8 |
--- ---
@ -435,16 +435,20 @@ PDF 翻譯引擎,支援多種輸出格式。
**輸入格式:** `pdf` **輸入格式:** `pdf`
**輸出格式7 種):** **輸出格式8 種):**
| OCR 語言 | 格式代碼 |
| -------- | -------- | | 格式代碼 | 說明 | 推薦場景 |
| 英文 | `pdf-en` | | ----------- | ------------------------------ | ---------------- |
| 繁體中文 | `pdf-zh-TW` | | `pdf-ocr` | **自動檢測(多語言)** ⭐ 推薦 | 通用、多語言文件 |
| 簡體中文 | `pdf-zh` | | `pdf-en` | 僅英文 | 純英文文件 |
| 日文 | `pdf-ja` | | `pdf-zh-TW` | 僅繁體中文 | 純繁體中文文件 |
| 韓文 | `pdf-ko` | | `pdf-zh` | 僅簡體中文 | 純簡體中文文件 |
| 德文 | `pdf-de` | | `pdf-ja` | 僅日文 | 純日文文件 |
| 法文 | `pdf-fr` | | `pdf-ko` | 僅韓文 | 純韓文文件 |
| `pdf-de` | 僅德文 | 純德文文件 |
| `pdf-fr` | 僅法文 | 純法文文件 |
> 💡 **自動多語言檢測**:選擇 `pdf-ocr` 可處理中英混排等多語言文件,無需手動指定語言。
--- ---

View file

@ -87,8 +87,9 @@ export async function convert(
const runMinerU = (useTableMode: boolean): Promise<void> => { const runMinerU = (useTableMode: boolean): Promise<void> => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// Build MinerU command arguments // Build MinerU command arguments
// MinerU CLI: mineru -p <input> -o <output_dir> -m auto // MinerU CLI: mineru -p <input> -o <output_dir> -m pipeline
const args = ["-p", filePath, "-o", mineruOutputDir, "-m", "auto"]; // 注意:使用 pipeline 模式而非 auto避免 VLM 模型未配置的問題
const args = ["-p", filePath, "-o", mineruOutputDir, "-m", "pipeline"];
// 表格模式支援(可能與某些 vLLM 版本不相容) // 表格模式支援(可能與某些 vLLM 版本不相容)
if (useTableMode) { if (useTableMode) {

View file

@ -18,12 +18,18 @@ import type { ExecFileFn } from "./types";
* - deu: 德文 * - deu: 德文
* - fra: 法文 * - fra: 法文
* *
* UI PDFMathTranslate * UI
* pdf-en, pdf-zh-TW, pdf-ja * - pdf-ocr: 自動檢測語言
* - pdf-en, pdf-zh-TW, pdf-ja
*
*
* 使 Tesseract eng+chi_tra+chi_sim+jpn+kor+deu+fra
*
*/ */
// 內建支援的 OCR 語言(與 PDFMathTranslate 風格一致) // 內建支援的 OCR 語言
const SUPPORTED_LANGUAGES = [ const SUPPORTED_LANGUAGES = [
"ocr", // 自動檢測(多語言)- 推薦
"en", // English "en", // English
"zh-TW", // 繁體中文 "zh-TW", // 繁體中文
"zh", // 簡體中文 "zh", // 簡體中文
@ -35,6 +41,10 @@ const SUPPORTED_LANGUAGES = [
// Tesseract 語言代碼映射UI 代碼 → Tesseract 代碼) // Tesseract 語言代碼映射UI 代碼 → Tesseract 代碼)
const LANG_MAP: Record<string, string> = { const LANG_MAP: Record<string, string> = {
// 自動檢測模式:使用所有已安裝的語言包
// 順序:英文優先(最常見),然後是 CJK 語言,最後是歐洲語言
ocr: "eng+chi_tra+chi_sim+jpn+kor+deu+fra",
auto: "eng+chi_tra+chi_sim+jpn+kor+deu+fra",
en: "eng", en: "eng",
"zh-TW": "chi_tra", "zh-TW": "chi_tra",
zh: "chi_sim", zh: "chi_sim",
@ -53,6 +63,11 @@ export const properties = {
}, },
}; };
// 建立大小寫不敏感的語言映射查找表
const LANG_MAP_LOWER: Record<string, string> = Object.fromEntries(
Object.entries(LANG_MAP).map(([k, v]) => [k.toLowerCase(), v]),
);
/** /**
* convertTo OCR * convertTo OCR
* @param convertTo "pdf-en" "pdf-zh-TW" * @param convertTo "pdf-en" "pdf-zh-TW"
@ -60,14 +75,14 @@ export const properties = {
*/ */
function extractOcrLanguage(convertTo: string): string { function extractOcrLanguage(convertTo: string): string {
// convertTo 格式: pdf-<lang> // convertTo 格式: pdf-<lang>
const match = convertTo.match(/^pdf-(.+)$/); const match = convertTo.match(/^pdf-(.+)$/i);
if (!match || !match[1]) { if (!match || !match[1]) {
throw new Error(`Invalid convertTo format: ${convertTo}. Expected pdf-<lang>`); throw new Error(`Invalid convertTo format: ${convertTo}. Expected pdf-<lang>`);
} }
const uiLang = match[1]; const uiLang = match[1];
// 轉換為 Tesseract 語言代碼 // 轉換為 Tesseract 語言代碼(大小寫不敏感查找)
const tessLang = LANG_MAP[uiLang] || uiLang.replace(/-/g, "_"); const tessLang = LANG_MAP_LOWER[uiLang.toLowerCase()] || uiLang.replace(/-/g, "_");
return tessLang; return tessLang;
} }
@ -111,7 +126,13 @@ function runOcrMyPdf(
// 階段 2準備 OCR 參數 // 階段 2準備 OCR 參數
console.log(`[OCRmyPDF] 📋 階段 2/5準備 OCR 參數`); console.log(`[OCRmyPDF] 📋 階段 2/5準備 OCR 參數`);
const isMultiLang = lang.includes("+");
if (isMultiLang) {
console.log(`[OCRmyPDF] ✅ OCR 模式: 多語言自動檢測`);
console.log(`[OCRmyPDF] ✅ 語言包: ${lang.split("+").join(", ")}`);
} else {
console.log(`[OCRmyPDF] ✅ OCR 語言: ${lang}`); console.log(`[OCRmyPDF] ✅ OCR 語言: ${lang}`);
}
const args = [ const args = [
"-l", "-l",

View file

@ -44,7 +44,7 @@ const tools = [
formatter: (s: string) => s.split("\n")[0], formatter: (s: string) => s.split("\n")[0],
}, },
{ {
cmd: "magick --version", cmd: "convert --version",
name: "ImageMagick", name: "ImageMagick",
errorMsg: "ImageMagick is not installed.", errorMsg: "ImageMagick is not installed.",
formatter: (s: string) => s.split("\n")[0]?.replace("Version: ", ""), formatter: (s: string) => s.split("\n")[0]?.replace("Version: ", ""),
@ -98,10 +98,10 @@ const tools = [
formatter: (s: string) => s.split("\n")[0], formatter: (s: string) => s.split("\n")[0],
}, },
{ {
cmd: "heif-info -v", cmd: "dpkg -l libheif1 | tail -1 | awk '{print $3}'",
name: "libheif", name: "libheif",
errorMsg: "libheif is not installed", errorMsg: "libheif is not installed",
formatter: (s: string) => `libheif v${s.split("\n")[0]}`, formatter: (s: string) => `libheif v${s.trim()}`,
}, },
{ {
cmd: "potrace -v", cmd: "potrace -v",
@ -116,10 +116,13 @@ const tools = [
formatter: (s: string) => s.split("\n")[0], formatter: (s: string) => s.split("\n")[0],
}, },
{ {
cmd: "msgconvert --version", cmd: "which msgconvert && dpkg -l libemail-outlook-message-perl | tail -1 | awk '{print $3}'",
name: "msgconvert", name: "msgconvert",
errorMsg: "msgconvert (libemail-outlook-message-perl) is not installed", errorMsg: "msgconvert (libemail-outlook-message-perl) is not installed",
formatter: (s: string) => s.split("\n")[0], formatter: (s: string) => {
const lines = s.split("\n").filter((l) => l.trim());
return `msgconvert v${lines[lines.length - 1] || "unknown"}`;
},
}, },
{ {
cmd: "bun -v", cmd: "bun -v",

File diff suppressed because it is too large Load diff