BabelDOC 0.2.7__tar.gz → 0.2.9__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {babeldoc-0.2.7 → babeldoc-0.2.9}/PKG-INFO +24 -28
- {babeldoc-0.2.7 → babeldoc-0.2.9}/README.md +15 -26
- babeldoc-0.2.9/babeldoc/__init__.py +1 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/const.py +1 -1
- {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/backend/pdf_creater.py +4 -4
- {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/midend/il_translator.py +1 -1
- {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/midend/il_translator_llm_only.py +18 -11
- {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/midend/styles_and_formulas.py +1 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/translator/translator.py +34 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/utils/fontmap.py +5 -1
- {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/docvision/doclayout.py +20 -24
- {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/high_level.py +349 -150
- {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/main.py +23 -7
- {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/progress_monitor.py +72 -4
- babeldoc-0.2.9/babeldoc/result_merger.py +130 -0
- babeldoc-0.2.9/babeldoc/split_manager.py +67 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/translation_config.py +70 -7
- {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/CONTRIBUTOR_REWARD.md +1 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/pyproject.toml +14 -3
- {babeldoc-0.2.7 → babeldoc-0.2.9}/uv.lock +243 -179
- babeldoc-0.2.7/babeldoc/__init__.py +0 -1
- {babeldoc-0.2.7 → babeldoc-0.2.9}/.cursorignore +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/.github/ISSUE_TEMPLATE/bug_report.yaml +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/.github/ISSUE_TEMPLATE/feature_request.yaml +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/.github/dependabot.yml +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/.github/labels.yml +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/.github/release-drafter.yml +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/.github/workflows/codeql.yml +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/.github/workflows/docs.yml +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/.github/workflows/labeler.yml +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/.github/workflows/publish-to-pypi.yml +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/.github/workflows/test.yml +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/.gitignore +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/.pre-commit-config.yaml +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/LICENSE +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/assets/assets.py +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/assets/embedding_assets_metadata.py +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/asynchronize/__init__.py +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/converter.py +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/__init__.py +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/babeldoc_exception/BabelDOCException.py +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/backend/__init__.py +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/frontend/__init__.py +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/frontend/il_creater.py +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/il_version_1.py +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/il_version_1.rnc +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/il_version_1.rng +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/il_version_1.xsd +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/midend/__init__.py +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/midend/add_debug_information.py +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/midend/detect_scanned_file.py +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/midend/layout_parser.py +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/midend/paragraph_finder.py +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/midend/remove_descent.py +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/midend/typesetting.py +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/translator/__init__.py +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/translator/cache.py +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/utils/layout_helper.py +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/utils/style_helper.py +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/xml_converter.py +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/docvision/README.md +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/docvision/__init__.py +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/docvision/rpc_doclayout.py +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/pdfinterp.py +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/tools/generate_font_metadata.py +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/CODE_OF_CONDUCT.md +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/CONTRIBUTING.md +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/ImplementationDetails/AsyncTranslate/AsyncTranslate.md +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/ImplementationDetails/ILTranslator/ILTranslator.md +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/ImplementationDetails/PDFCreation/PDFCreation.md +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/ImplementationDetails/PDFParsing/PDFParsing.md +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/ImplementationDetails/ParagraphFinding/ParagraphFinding.md +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/ImplementationDetails/README.md +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/ImplementationDetails/StylesAndFormulas/StylesAndFormulas.md +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/ImplementationDetails/Typesetting/Typesetting.md +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/README.md +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/deploy.sh +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/images/babeldoc-banner.png +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/images/babeldoc-big-logo-darkmode-with-transparent-background.png +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/images/babeldoc-big-logo-darkmode-with-transparent-background.svg +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/images/babeldoc-big-logo-with-transparent-background.png +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/images/babeldoc-big-logo-with-transparent-background.svg +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/images/babeldoc-big-logo.png +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/images/babeldoc-contributor_reward_example.png +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/images/babeldoc-preview.gif +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/images/babeldoc-small-logo-with-transparent-background.png +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/images/babeldoc-small-logo-with-transparent-background.svg +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/images/babeldoc-small-logo.png +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/index.md +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/intro-to-pdf-object.md +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/requirements.txt +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/mkdocs.yml +0 -0
- {babeldoc-0.2.7 → babeldoc-0.2.9}/tests/test_translation_config.py +0 -0
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: BabelDOC
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.9
|
|
4
4
|
Summary: Yet Another Document Translator
|
|
5
5
|
Project-URL: Homepage, https://github.com/funstory-ai/BabelDOC
|
|
6
6
|
Project-URL: Issues, https://github.com/funstory-ai/BabelDOC/issues
|
|
7
|
+
Author-email: awwaawwa <aw@funstory.ai>
|
|
8
|
+
Maintainer-email: awwaawwa <aw@funstory.ai>
|
|
7
9
|
License-Expression: AGPL-3.0
|
|
8
10
|
License-File: LICENSE
|
|
9
11
|
Keywords: PDF
|
|
@@ -20,10 +22,11 @@ Requires-Dist: numpy>=2.0.2
|
|
|
20
22
|
Requires-Dist: onnx>=1.17.0
|
|
21
23
|
Requires-Dist: onnxruntime>=1.16.1
|
|
22
24
|
Requires-Dist: openai>=1.59.3
|
|
23
|
-
Requires-Dist: opencv-python>=4.10.0.84
|
|
25
|
+
Requires-Dist: opencv-python-headless>=4.10.0.84
|
|
24
26
|
Requires-Dist: orjson>=3.10.14
|
|
25
27
|
Requires-Dist: pdfminer-six>=20240706
|
|
26
28
|
Requires-Dist: peewee>=3.17.8
|
|
29
|
+
Requires-Dist: psutil>=7.0.0
|
|
27
30
|
Requires-Dist: pydantic>=2.10.6
|
|
28
31
|
Requires-Dist: pymupdf>=1.25.1
|
|
29
32
|
Requires-Dist: python-levenshtein>=0.27.1
|
|
@@ -34,6 +37,10 @@ Requires-Dist: tiktoken>=0.9.0
|
|
|
34
37
|
Requires-Dist: toml>=0.10.2
|
|
35
38
|
Requires-Dist: tqdm>=4.67.1
|
|
36
39
|
Requires-Dist: xsdata[cli,lxml,soap]>=24.12
|
|
40
|
+
Provides-Extra: cuda
|
|
41
|
+
Requires-Dist: onnxruntime-gpu>=1.16.1; extra == 'cuda'
|
|
42
|
+
Provides-Extra: directml
|
|
43
|
+
Requires-Dist: onnxruntime-directml>=1.16.1; extra == 'directml'
|
|
37
44
|
Description-Content-Type: text/markdown
|
|
38
45
|
|
|
39
46
|
<!-- # Yet Another Document Translator -->
|
|
@@ -172,6 +179,7 @@ uv run babeldoc --files example.pdf --files example2.pdf --openai --openai-model
|
|
|
172
179
|
- `--enhance-compatibility`: Enable all compatibility enhancement options (equivalent to --skip-clean --dual-translate-first --disable-rich-text-translate)
|
|
173
180
|
- `--use-alternating-pages-dual`: Use alternating pages mode for dual PDF. When enabled, original and translated pages are arranged in alternate order. When disabled (default), original and translated pages are shown side by side on the same page.
|
|
174
181
|
- `--watermark-output-mode`: Control watermark output mode: 'watermarked' (default) adds watermark to translated PDF, 'no_watermark' doesn't add watermark, 'both' outputs both versions.
|
|
182
|
+
- `--max-pages-per-part`: Maximum number of pages per part for split translation. If not set, no splitting will be performed.
|
|
175
183
|
- `--no-watermark`: [DEPRECATED] Use --watermark-output-mode=no_watermark instead.
|
|
176
184
|
|
|
177
185
|
> [!TIP]
|
|
@@ -179,6 +187,7 @@ uv run babeldoc --files example.pdf --files example2.pdf --openai --openai-model
|
|
|
179
187
|
> - `--disable-rich-text-translate` can also help with compatibility by simplifying translation input
|
|
180
188
|
> - However, using `--skip-clean` will result in larger file sizes
|
|
181
189
|
> - If you encounter any compatibility issues, try using `--enhance-compatibility` first
|
|
190
|
+
> - Use `--max-pages-per-part` for large documents to split them into smaller parts for translation and automatically merge them back.
|
|
182
191
|
|
|
183
192
|
### Translation Service Options
|
|
184
193
|
|
|
@@ -234,26 +243,6 @@ uv run babeldoc --files example.pdf --files example2.pdf --openai --openai-model
|
|
|
234
243
|
|
|
235
244
|
Example Configuration:
|
|
236
245
|
|
|
237
|
-
```toml
|
|
238
|
-
[babeldoc]
|
|
239
|
-
debug = true
|
|
240
|
-
lang-in = "en-US"
|
|
241
|
-
lang-out = "zh-CN"
|
|
242
|
-
qps = 20
|
|
243
|
-
# this is a comment
|
|
244
|
-
# pages = 4
|
|
245
|
-
openai = true
|
|
246
|
-
openai-model = "SOME_ALSOME_MODEL"
|
|
247
|
-
openai-base-url = "https://example.example/v1"
|
|
248
|
-
openai-api-key = "[KEY]"
|
|
249
|
-
# Offline assets management
|
|
250
|
-
# generate-offline-assets = "/path/to/output/dir"
|
|
251
|
-
# restore-offline-assets = "/path/to/offline_assets_package.zip"
|
|
252
|
-
# All other options can also be set in the configuration file.
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
For a more comprehensive configuration example with offline assets management:
|
|
256
|
-
|
|
257
246
|
```toml
|
|
258
247
|
[babeldoc]
|
|
259
248
|
# Basic settings
|
|
@@ -263,12 +252,6 @@ lang-out = "zh-CN"
|
|
|
263
252
|
qps = 10
|
|
264
253
|
output = "/path/to/output/dir"
|
|
265
254
|
|
|
266
|
-
# Translation service
|
|
267
|
-
openai = true
|
|
268
|
-
openai-model = "gpt-4o-mini"
|
|
269
|
-
openai-base-url = "https://api.openai.com/v1"
|
|
270
|
-
openai-api-key = "your-api-key-here"
|
|
271
|
-
|
|
272
255
|
# PDF processing options
|
|
273
256
|
split-short-lines = false
|
|
274
257
|
short-line-split-factor = 0.8
|
|
@@ -277,8 +260,15 @@ dual-translate-first = false
|
|
|
277
260
|
disable-rich-text-translate = false
|
|
278
261
|
use-alternating-pages-dual = false
|
|
279
262
|
watermark-output-mode = "watermarked" # Choices: "watermarked", "no_watermark", "both"
|
|
263
|
+
max-pages-per-part = 50 # Automatically split the document for translation and merge it back.
|
|
280
264
|
# no-watermark = false # DEPRECATED: Use watermark-output-mode instead
|
|
281
265
|
|
|
266
|
+
# Translation service
|
|
267
|
+
openai = true
|
|
268
|
+
openai-model = "gpt-4o-mini"
|
|
269
|
+
openai-base-url = "https://api.openai.com/v1"
|
|
270
|
+
openai-api-key = "your-api-key-here"
|
|
271
|
+
|
|
282
272
|
# Output control
|
|
283
273
|
no-dual = false
|
|
284
274
|
no-mono = false
|
|
@@ -293,6 +283,12 @@ report-interval = 0.5
|
|
|
293
283
|
|
|
294
284
|
## Python API
|
|
295
285
|
|
|
286
|
+
> [!TIP]
|
|
287
|
+
>
|
|
288
|
+
> 1. Before pdf2zh 2.0 is released, you can temporarily use BabelDOC's Python API. However, after pdf2zh 2.0 is released, please directly use pdf2zh's Python API.
|
|
289
|
+
>
|
|
290
|
+
> 2. This project's Python API does not guarantee any compatibility. However, the Python API from pdf2zh will guarantee a certain level of compatibility.
|
|
291
|
+
|
|
296
292
|
You can refer to the example in [main.py](https://github.com/funstory-ai/yadt/blob/main/babeldoc/main.py) to use BabelDOC's Python API.
|
|
297
293
|
|
|
298
294
|
Please note:
|
|
@@ -134,6 +134,7 @@ uv run babeldoc --files example.pdf --files example2.pdf --openai --openai-model
|
|
|
134
134
|
- `--enhance-compatibility`: Enable all compatibility enhancement options (equivalent to --skip-clean --dual-translate-first --disable-rich-text-translate)
|
|
135
135
|
- `--use-alternating-pages-dual`: Use alternating pages mode for dual PDF. When enabled, original and translated pages are arranged in alternate order. When disabled (default), original and translated pages are shown side by side on the same page.
|
|
136
136
|
- `--watermark-output-mode`: Control watermark output mode: 'watermarked' (default) adds watermark to translated PDF, 'no_watermark' doesn't add watermark, 'both' outputs both versions.
|
|
137
|
+
- `--max-pages-per-part`: Maximum number of pages per part for split translation. If not set, no splitting will be performed.
|
|
137
138
|
- `--no-watermark`: [DEPRECATED] Use --watermark-output-mode=no_watermark instead.
|
|
138
139
|
|
|
139
140
|
> [!TIP]
|
|
@@ -141,6 +142,7 @@ uv run babeldoc --files example.pdf --files example2.pdf --openai --openai-model
|
|
|
141
142
|
> - `--disable-rich-text-translate` can also help with compatibility by simplifying translation input
|
|
142
143
|
> - However, using `--skip-clean` will result in larger file sizes
|
|
143
144
|
> - If you encounter any compatibility issues, try using `--enhance-compatibility` first
|
|
145
|
+
> - Use `--max-pages-per-part` for large documents to split them into smaller parts for translation and automatically merge them back.
|
|
144
146
|
|
|
145
147
|
### Translation Service Options
|
|
146
148
|
|
|
@@ -196,26 +198,6 @@ uv run babeldoc --files example.pdf --files example2.pdf --openai --openai-model
|
|
|
196
198
|
|
|
197
199
|
Example Configuration:
|
|
198
200
|
|
|
199
|
-
```toml
|
|
200
|
-
[babeldoc]
|
|
201
|
-
debug = true
|
|
202
|
-
lang-in = "en-US"
|
|
203
|
-
lang-out = "zh-CN"
|
|
204
|
-
qps = 20
|
|
205
|
-
# this is a comment
|
|
206
|
-
# pages = 4
|
|
207
|
-
openai = true
|
|
208
|
-
openai-model = "SOME_ALSOME_MODEL"
|
|
209
|
-
openai-base-url = "https://example.example/v1"
|
|
210
|
-
openai-api-key = "[KEY]"
|
|
211
|
-
# Offline assets management
|
|
212
|
-
# generate-offline-assets = "/path/to/output/dir"
|
|
213
|
-
# restore-offline-assets = "/path/to/offline_assets_package.zip"
|
|
214
|
-
# All other options can also be set in the configuration file.
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
For a more comprehensive configuration example with offline assets management:
|
|
218
|
-
|
|
219
201
|
```toml
|
|
220
202
|
[babeldoc]
|
|
221
203
|
# Basic settings
|
|
@@ -225,12 +207,6 @@ lang-out = "zh-CN"
|
|
|
225
207
|
qps = 10
|
|
226
208
|
output = "/path/to/output/dir"
|
|
227
209
|
|
|
228
|
-
# Translation service
|
|
229
|
-
openai = true
|
|
230
|
-
openai-model = "gpt-4o-mini"
|
|
231
|
-
openai-base-url = "https://api.openai.com/v1"
|
|
232
|
-
openai-api-key = "your-api-key-here"
|
|
233
|
-
|
|
234
210
|
# PDF processing options
|
|
235
211
|
split-short-lines = false
|
|
236
212
|
short-line-split-factor = 0.8
|
|
@@ -239,8 +215,15 @@ dual-translate-first = false
|
|
|
239
215
|
disable-rich-text-translate = false
|
|
240
216
|
use-alternating-pages-dual = false
|
|
241
217
|
watermark-output-mode = "watermarked" # Choices: "watermarked", "no_watermark", "both"
|
|
218
|
+
max-pages-per-part = 50 # Automatically split the document for translation and merge it back.
|
|
242
219
|
# no-watermark = false # DEPRECATED: Use watermark-output-mode instead
|
|
243
220
|
|
|
221
|
+
# Translation service
|
|
222
|
+
openai = true
|
|
223
|
+
openai-model = "gpt-4o-mini"
|
|
224
|
+
openai-base-url = "https://api.openai.com/v1"
|
|
225
|
+
openai-api-key = "your-api-key-here"
|
|
226
|
+
|
|
244
227
|
# Output control
|
|
245
228
|
no-dual = false
|
|
246
229
|
no-mono = false
|
|
@@ -255,6 +238,12 @@ report-interval = 0.5
|
|
|
255
238
|
|
|
256
239
|
## Python API
|
|
257
240
|
|
|
241
|
+
> [!TIP]
|
|
242
|
+
>
|
|
243
|
+
> 1. Before pdf2zh 2.0 is released, you can temporarily use BabelDOC's Python API. However, after pdf2zh 2.0 is released, please directly use pdf2zh's Python API.
|
|
244
|
+
>
|
|
245
|
+
> 2. This project's Python API does not guarantee any compatibility. However, the Python API from pdf2zh will guarantee a certain level of compatibility.
|
|
246
|
+
|
|
258
247
|
You can refer to the example in [main.py](https://github.com/funstory-ai/yadt/blob/main/babeldoc/main.py) to use BabelDOC's Python API.
|
|
259
248
|
|
|
260
249
|
Please note:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.2.9"
|
|
@@ -552,7 +552,7 @@ class PDFCreater:
|
|
|
552
552
|
while process.is_alive():
|
|
553
553
|
if time.time() - start_time > timeout:
|
|
554
554
|
logger.warning(
|
|
555
|
-
f"PDF save with clean=
|
|
555
|
+
f"PDF save with clean={clean} timeout after {timeout} seconds, terminating subprocess"
|
|
556
556
|
)
|
|
557
557
|
process.terminate()
|
|
558
558
|
try:
|
|
@@ -569,7 +569,7 @@ class PDFCreater:
|
|
|
569
569
|
logger.error(f"Error terminating PDF save process: {e}")
|
|
570
570
|
|
|
571
571
|
# Fallback to save without clean parameter
|
|
572
|
-
logger.info("Falling back to save
|
|
572
|
+
logger.info("Falling back to save with clean=False")
|
|
573
573
|
try:
|
|
574
574
|
pdf.save(
|
|
575
575
|
output_path,
|
|
@@ -598,7 +598,7 @@ class PDFCreater:
|
|
|
598
598
|
and Path(temp_output).exists()
|
|
599
599
|
and Path(temp_output).stat().st_size > 0
|
|
600
600
|
):
|
|
601
|
-
logger.info("PDF save with clean=
|
|
601
|
+
logger.info(f"PDF save with clean={clean} completed successfully")
|
|
602
602
|
# Copy the successfully created file to the target path
|
|
603
603
|
try:
|
|
604
604
|
import shutil
|
|
@@ -614,7 +614,7 @@ class PDFCreater:
|
|
|
614
614
|
Path(temp_output).unlink()
|
|
615
615
|
else:
|
|
616
616
|
logger.warning(
|
|
617
|
-
f"PDF save with clean=
|
|
617
|
+
f"PDF save with clean={clean} failed with exit code {exit_code} or produced empty file"
|
|
618
618
|
)
|
|
619
619
|
# Fallback to save without clean parameter
|
|
620
620
|
try:
|
|
@@ -603,7 +603,7 @@ class ILTranslator:
|
|
|
603
603
|
)
|
|
604
604
|
except Exception as e:
|
|
605
605
|
logger.exception(
|
|
606
|
-
f"Error translating paragraph. Paragraph: {paragraph}. Error: {e}. ",
|
|
606
|
+
f"Error translating paragraph. Paragraph: {paragraph.debug_id} ({paragraph.unicode}). Error: {e}. ",
|
|
607
607
|
)
|
|
608
608
|
# ignore error and continue
|
|
609
609
|
return
|
|
@@ -43,6 +43,9 @@ class ILTranslatorLLMOnly:
|
|
|
43
43
|
self.translate_engine = translate_engine
|
|
44
44
|
self.translation_config = translation_config
|
|
45
45
|
self.font_mapper = FontMapper(translation_config)
|
|
46
|
+
self.shared_context_cross_split_part = (
|
|
47
|
+
translation_config.shared_context_cross_split_part
|
|
48
|
+
)
|
|
46
49
|
|
|
47
50
|
self.il_translator = ILTranslator(
|
|
48
51
|
translate_engine=translate_engine,
|
|
@@ -73,8 +76,15 @@ class ILTranslatorLLMOnly:
|
|
|
73
76
|
def translate(self, docs: Document) -> None:
|
|
74
77
|
tracker = DocumentTranslateTracker()
|
|
75
78
|
|
|
76
|
-
|
|
77
|
-
|
|
79
|
+
if not self.translation_config.shared_context_cross_split_part.first_paragraph:
|
|
80
|
+
# Try to find the first title paragraph
|
|
81
|
+
title_paragraph = self.find_title_paragraph(docs)
|
|
82
|
+
self.translation_config.shared_context_cross_split_part.first_paragraph = (
|
|
83
|
+
title_paragraph
|
|
84
|
+
)
|
|
85
|
+
self.translation_config.shared_context_cross_split_part.recent_title_paragraph = title_paragraph
|
|
86
|
+
if title_paragraph:
|
|
87
|
+
logger.info(f"Found first title paragraph: {title_paragraph.unicode}")
|
|
78
88
|
|
|
79
89
|
# count total paragraph
|
|
80
90
|
total = sum(
|
|
@@ -105,7 +115,6 @@ class ILTranslatorLLMOnly:
|
|
|
105
115
|
executor,
|
|
106
116
|
pbar,
|
|
107
117
|
tracker.new_page(),
|
|
108
|
-
title_paragraph,
|
|
109
118
|
executor2,
|
|
110
119
|
)
|
|
111
120
|
|
|
@@ -122,7 +131,6 @@ class ILTranslatorLLMOnly:
|
|
|
122
131
|
executor: concurrent.futures.ThreadPoolExecutor,
|
|
123
132
|
pbar: tqdm | None = None,
|
|
124
133
|
tracker: PageTranslateTracker = None,
|
|
125
|
-
title_paragraph: PdfParagraph | None = None,
|
|
126
134
|
executor2: concurrent.futures.ThreadPoolExecutor | None = None,
|
|
127
135
|
):
|
|
128
136
|
self.translation_config.raise_if_cancelled()
|
|
@@ -136,7 +144,6 @@ class ILTranslatorLLMOnly:
|
|
|
136
144
|
page_xobj_font_map[xobj.xobj_id][font.font_id] = font
|
|
137
145
|
|
|
138
146
|
paragraphs = []
|
|
139
|
-
local_title_paragraph = None
|
|
140
147
|
|
|
141
148
|
total_unicode_counts = 0
|
|
142
149
|
for paragraph in page.pdf_paragraph:
|
|
@@ -146,7 +153,7 @@ class ILTranslatorLLMOnly:
|
|
|
146
153
|
total_unicode_counts += len(paragraph.unicode)
|
|
147
154
|
paragraphs.append(paragraph)
|
|
148
155
|
if paragraph.layout_label == "title":
|
|
149
|
-
|
|
156
|
+
self.shared_context_cross_split_part.recent_title_paragraph = paragraph
|
|
150
157
|
|
|
151
158
|
if total_unicode_counts > 1200 or len(paragraphs) > 4:
|
|
152
159
|
executor.submit(
|
|
@@ -155,8 +162,8 @@ class ILTranslatorLLMOnly:
|
|
|
155
162
|
pbar,
|
|
156
163
|
page_font_map,
|
|
157
164
|
page_xobj_font_map,
|
|
158
|
-
|
|
159
|
-
|
|
165
|
+
self.translation_config.shared_context_cross_split_part.first_paragraph,
|
|
166
|
+
self.translation_config.shared_context_cross_split_part.recent_title_paragraph,
|
|
160
167
|
executor2,
|
|
161
168
|
)
|
|
162
169
|
paragraphs = []
|
|
@@ -169,8 +176,8 @@ class ILTranslatorLLMOnly:
|
|
|
169
176
|
pbar,
|
|
170
177
|
page_font_map,
|
|
171
178
|
page_xobj_font_map,
|
|
172
|
-
|
|
173
|
-
|
|
179
|
+
self.translation_config.shared_context_cross_split_part.first_paragraph,
|
|
180
|
+
self.translation_config.shared_context_cross_split_part.recent_title_paragraph,
|
|
174
181
|
executor2,
|
|
175
182
|
)
|
|
176
183
|
|
|
@@ -328,9 +335,9 @@ class ILTranslatorLLMOnly:
|
|
|
328
335
|
translate_input,
|
|
329
336
|
translated_text,
|
|
330
337
|
)
|
|
338
|
+
should_fallback = False
|
|
331
339
|
if pbar:
|
|
332
340
|
pbar.advance(1)
|
|
333
|
-
should_fallback = False
|
|
334
341
|
except Exception as e:
|
|
335
342
|
logger.exception(f"Error translating paragraph. Error: {e}.")
|
|
336
343
|
# Ignore error and continue
|
|
@@ -62,6 +62,31 @@ def set_translate_rate_limiter(max_qps):
|
|
|
62
62
|
_translate_rate_limiter.set_max_qps(max_qps)
|
|
63
63
|
|
|
64
64
|
|
|
65
|
+
class AtomicInteger:
|
|
66
|
+
def __init__(self, value=0):
|
|
67
|
+
self._value = int(value)
|
|
68
|
+
self._lock = threading.Lock()
|
|
69
|
+
|
|
70
|
+
def inc(self, d=1):
|
|
71
|
+
with self._lock:
|
|
72
|
+
self._value += int(d)
|
|
73
|
+
return self._value
|
|
74
|
+
|
|
75
|
+
def dec(self, d=1):
|
|
76
|
+
return self.inc(-d)
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
def value(self):
|
|
80
|
+
with self._lock:
|
|
81
|
+
return self._value
|
|
82
|
+
|
|
83
|
+
@value.setter
|
|
84
|
+
def value(self, v):
|
|
85
|
+
with self._lock:
|
|
86
|
+
self._value = int(v)
|
|
87
|
+
return self._value
|
|
88
|
+
|
|
89
|
+
|
|
65
90
|
class BaseTranslator(ABC):
|
|
66
91
|
# Due to cache limitations, name should be within 20 characters.
|
|
67
92
|
# cache.py: translate_engine = CharField(max_length=20)
|
|
@@ -195,6 +220,9 @@ class OpenAITranslator(BaseTranslator):
|
|
|
195
220
|
self.model = model
|
|
196
221
|
self.add_cache_impact_parameters("model", self.model)
|
|
197
222
|
self.add_cache_impact_parameters("prompt", self.prompt(""))
|
|
223
|
+
self.token_count = AtomicInteger()
|
|
224
|
+
self.prompt_token_count = AtomicInteger()
|
|
225
|
+
self.completion_token_count = AtomicInteger()
|
|
198
226
|
|
|
199
227
|
@retry(
|
|
200
228
|
retry=retry_if_exception_type(openai.RateLimitError),
|
|
@@ -211,6 +239,9 @@ class OpenAITranslator(BaseTranslator):
|
|
|
211
239
|
**self.options,
|
|
212
240
|
messages=self.prompt(text),
|
|
213
241
|
)
|
|
242
|
+
self.token_count.inc(response.usage.total_tokens)
|
|
243
|
+
self.prompt_token_count.inc(response.usage.prompt_tokens)
|
|
244
|
+
self.completion_token_count.inc(response.usage.completion_tokens)
|
|
214
245
|
return response.choices[0].message.content.strip()
|
|
215
246
|
|
|
216
247
|
def prompt(self, text):
|
|
@@ -248,6 +279,9 @@ class OpenAITranslator(BaseTranslator):
|
|
|
248
279
|
},
|
|
249
280
|
],
|
|
250
281
|
)
|
|
282
|
+
self.token_count.inc(response.usage.total_tokens)
|
|
283
|
+
self.prompt_token_count.inc(response.usage.prompt_tokens)
|
|
284
|
+
self.completion_token_count.inc(response.usage.completion_tokens)
|
|
251
285
|
return response.choices[0].message.content.strip()
|
|
252
286
|
|
|
253
287
|
def get_formular_placeholder(self, placeholder_id: int):
|
|
@@ -172,10 +172,14 @@ class FontMapper:
|
|
|
172
172
|
|
|
173
173
|
font_id = {}
|
|
174
174
|
xreflen = doc_zh.xref_length()
|
|
175
|
+
total = xreflen - 1 + len(font_list) + len(il.page) + len(font_list)
|
|
175
176
|
with self.translation_config.progress_monitor.stage_start(
|
|
176
177
|
self.stage_name,
|
|
177
|
-
|
|
178
|
+
total,
|
|
178
179
|
) as pbar:
|
|
180
|
+
if not il.page:
|
|
181
|
+
pbar.advance(total)
|
|
182
|
+
return
|
|
179
183
|
for font in font_list:
|
|
180
184
|
if font[0] in font_id:
|
|
181
185
|
continue
|
|
@@ -2,12 +2,22 @@ import abc
|
|
|
2
2
|
import ast
|
|
3
3
|
import logging
|
|
4
4
|
import platform
|
|
5
|
+
import re
|
|
5
6
|
from collections.abc import Generator
|
|
6
7
|
|
|
7
8
|
import cv2
|
|
8
9
|
import numpy as np
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
import onnx
|
|
13
|
+
import onnxruntime
|
|
14
|
+
except ImportError as e:
|
|
15
|
+
if "DLL load failed" in str(e):
|
|
16
|
+
raise OSError(
|
|
17
|
+
"Microsoft Visual C++ Redistributable is not installed. "
|
|
18
|
+
"Download it at https://aka.ms/vs/17/release/vc_redist.x64.exe"
|
|
19
|
+
) from e
|
|
20
|
+
raise
|
|
11
21
|
import pymupdf
|
|
12
22
|
|
|
13
23
|
import babeldoc.document_il.il_version_1
|
|
@@ -91,26 +101,6 @@ class YoloBox:
|
|
|
91
101
|
# 检测操作系统类型
|
|
92
102
|
os_name = platform.system()
|
|
93
103
|
|
|
94
|
-
providers = []
|
|
95
|
-
|
|
96
|
-
if os_name == "Darwin" and False: # Temporarily disable CoreML due to some issues
|
|
97
|
-
providers.append(
|
|
98
|
-
(
|
|
99
|
-
"CoreMLExecutionProvider",
|
|
100
|
-
{
|
|
101
|
-
"ModelFormat": "MLProgram",
|
|
102
|
-
"MLComputeUnits": "ALL",
|
|
103
|
-
"RequireStaticInputShapes": "0",
|
|
104
|
-
"EnableOnSubgraphs": "0",
|
|
105
|
-
},
|
|
106
|
-
),
|
|
107
|
-
)
|
|
108
|
-
# workaround for CoreML batch inference issues
|
|
109
|
-
max_batch_size = 1
|
|
110
|
-
else:
|
|
111
|
-
max_batch_size = 1
|
|
112
|
-
providers.append("CPUExecutionProvider") # CPU 执行提供者作为通用后备选项
|
|
113
|
-
|
|
114
104
|
|
|
115
105
|
class OnnxModel(DocLayoutModel):
|
|
116
106
|
def __init__(self, model_path: str):
|
|
@@ -120,7 +110,13 @@ class OnnxModel(DocLayoutModel):
|
|
|
120
110
|
metadata = {d.key: d.value for d in model.metadata_props}
|
|
121
111
|
self._stride = ast.literal_eval(metadata["stride"])
|
|
122
112
|
self._names = ast.literal_eval(metadata["names"])
|
|
113
|
+
providers = []
|
|
123
114
|
|
|
115
|
+
available_providers = onnxruntime.get_available_providers()
|
|
116
|
+
for provider in available_providers:
|
|
117
|
+
if re.match(r"dml|cuda|coreml|cpu", provider, re.IGNORECASE):
|
|
118
|
+
logger.info(f"Available Provider: {provider}")
|
|
119
|
+
providers.append(provider)
|
|
124
120
|
self.model = onnxruntime.InferenceSession(
|
|
125
121
|
model.SerializeToString(),
|
|
126
122
|
providers=providers,
|
|
@@ -228,7 +224,7 @@ class OnnxModel(DocLayoutModel):
|
|
|
228
224
|
|
|
229
225
|
total_images = len(image)
|
|
230
226
|
results = []
|
|
231
|
-
batch_size =
|
|
227
|
+
batch_size = 1
|
|
232
228
|
|
|
233
229
|
# Process images in batches
|
|
234
230
|
for i in range(0, total_images, batch_size):
|
|
@@ -237,7 +233,7 @@ class OnnxModel(DocLayoutModel):
|
|
|
237
233
|
|
|
238
234
|
# Calculate target size based on the maximum height in the batch
|
|
239
235
|
max_height = max(img.shape[0] for img in batch_images)
|
|
240
|
-
target_imgsz =
|
|
236
|
+
target_imgsz = 1024
|
|
241
237
|
|
|
242
238
|
# Preprocess batch
|
|
243
239
|
processed_batch = []
|