Add comprehensive format integrity tests for various converters
This commit is contained in:
parent
47968f6c80
commit
80ab233d15
8 changed files with 1384 additions and 192 deletions
28
README.md
28
README.md
|
|
@ -6,7 +6,8 @@
|
||||||
|
|
||||||
[](https://hub.docker.com/r/convertx/convertx-cn)
|
[](https://hub.docker.com/r/convertx/convertx-cn)
|
||||||
[](https://github.com/pi-docket/ConvertX-CN/releases)
|
[](https://github.com/pi-docket/ConvertX-CN/releases)
|
||||||
[](LICENSE)
|

|
||||||
|

|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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`),可能獲得更高的辨識準確度。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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` 可處理中英混排等多語言文件,無需手動指定語言。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
1123
tests/converters/formats.test.ts
Normal file
1123
tests/converters/formats.test.ts
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue