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.
Files changed (93) hide show
  1. {babeldoc-0.2.7 → babeldoc-0.2.9}/PKG-INFO +24 -28
  2. {babeldoc-0.2.7 → babeldoc-0.2.9}/README.md +15 -26
  3. babeldoc-0.2.9/babeldoc/__init__.py +1 -0
  4. {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/const.py +1 -1
  5. {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/backend/pdf_creater.py +4 -4
  6. {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/midend/il_translator.py +1 -1
  7. {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/midend/il_translator_llm_only.py +18 -11
  8. {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/midend/styles_and_formulas.py +1 -0
  9. {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/translator/translator.py +34 -0
  10. {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/utils/fontmap.py +5 -1
  11. {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/docvision/doclayout.py +20 -24
  12. {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/high_level.py +349 -150
  13. {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/main.py +23 -7
  14. {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/progress_monitor.py +72 -4
  15. babeldoc-0.2.9/babeldoc/result_merger.py +130 -0
  16. babeldoc-0.2.9/babeldoc/split_manager.py +67 -0
  17. {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/translation_config.py +70 -7
  18. {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/CONTRIBUTOR_REWARD.md +1 -0
  19. {babeldoc-0.2.7 → babeldoc-0.2.9}/pyproject.toml +14 -3
  20. {babeldoc-0.2.7 → babeldoc-0.2.9}/uv.lock +243 -179
  21. babeldoc-0.2.7/babeldoc/__init__.py +0 -1
  22. {babeldoc-0.2.7 → babeldoc-0.2.9}/.cursorignore +0 -0
  23. {babeldoc-0.2.7 → babeldoc-0.2.9}/.github/ISSUE_TEMPLATE/bug_report.yaml +0 -0
  24. {babeldoc-0.2.7 → babeldoc-0.2.9}/.github/ISSUE_TEMPLATE/feature_request.yaml +0 -0
  25. {babeldoc-0.2.7 → babeldoc-0.2.9}/.github/dependabot.yml +0 -0
  26. {babeldoc-0.2.7 → babeldoc-0.2.9}/.github/labels.yml +0 -0
  27. {babeldoc-0.2.7 → babeldoc-0.2.9}/.github/release-drafter.yml +0 -0
  28. {babeldoc-0.2.7 → babeldoc-0.2.9}/.github/workflows/codeql.yml +0 -0
  29. {babeldoc-0.2.7 → babeldoc-0.2.9}/.github/workflows/docs.yml +0 -0
  30. {babeldoc-0.2.7 → babeldoc-0.2.9}/.github/workflows/labeler.yml +0 -0
  31. {babeldoc-0.2.7 → babeldoc-0.2.9}/.github/workflows/publish-to-pypi.yml +0 -0
  32. {babeldoc-0.2.7 → babeldoc-0.2.9}/.github/workflows/test.yml +0 -0
  33. {babeldoc-0.2.7 → babeldoc-0.2.9}/.gitignore +0 -0
  34. {babeldoc-0.2.7 → babeldoc-0.2.9}/.pre-commit-config.yaml +0 -0
  35. {babeldoc-0.2.7 → babeldoc-0.2.9}/LICENSE +0 -0
  36. {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/assets/assets.py +0 -0
  37. {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/assets/embedding_assets_metadata.py +0 -0
  38. {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/asynchronize/__init__.py +0 -0
  39. {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/converter.py +0 -0
  40. {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/__init__.py +0 -0
  41. {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/babeldoc_exception/BabelDOCException.py +0 -0
  42. {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/backend/__init__.py +0 -0
  43. {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/frontend/__init__.py +0 -0
  44. {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/frontend/il_creater.py +0 -0
  45. {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/il_version_1.py +0 -0
  46. {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/il_version_1.rnc +0 -0
  47. {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/il_version_1.rng +0 -0
  48. {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/il_version_1.xsd +0 -0
  49. {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/midend/__init__.py +0 -0
  50. {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/midend/add_debug_information.py +0 -0
  51. {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/midend/detect_scanned_file.py +0 -0
  52. {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/midend/layout_parser.py +0 -0
  53. {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/midend/paragraph_finder.py +0 -0
  54. {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/midend/remove_descent.py +0 -0
  55. {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/midend/typesetting.py +0 -0
  56. {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/translator/__init__.py +0 -0
  57. {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/translator/cache.py +0 -0
  58. {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/utils/layout_helper.py +0 -0
  59. {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/utils/style_helper.py +0 -0
  60. {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/document_il/xml_converter.py +0 -0
  61. {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/docvision/README.md +0 -0
  62. {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/docvision/__init__.py +0 -0
  63. {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/docvision/rpc_doclayout.py +0 -0
  64. {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/pdfinterp.py +0 -0
  65. {babeldoc-0.2.7 → babeldoc-0.2.9}/babeldoc/tools/generate_font_metadata.py +0 -0
  66. {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/CODE_OF_CONDUCT.md +0 -0
  67. {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/CONTRIBUTING.md +0 -0
  68. {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/ImplementationDetails/AsyncTranslate/AsyncTranslate.md +0 -0
  69. {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/ImplementationDetails/ILTranslator/ILTranslator.md +0 -0
  70. {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/ImplementationDetails/PDFCreation/PDFCreation.md +0 -0
  71. {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/ImplementationDetails/PDFParsing/PDFParsing.md +0 -0
  72. {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/ImplementationDetails/ParagraphFinding/ParagraphFinding.md +0 -0
  73. {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/ImplementationDetails/README.md +0 -0
  74. {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/ImplementationDetails/StylesAndFormulas/StylesAndFormulas.md +0 -0
  75. {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/ImplementationDetails/Typesetting/Typesetting.md +0 -0
  76. {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/README.md +0 -0
  77. {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/deploy.sh +0 -0
  78. {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/images/babeldoc-banner.png +0 -0
  79. {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/images/babeldoc-big-logo-darkmode-with-transparent-background.png +0 -0
  80. {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/images/babeldoc-big-logo-darkmode-with-transparent-background.svg +0 -0
  81. {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/images/babeldoc-big-logo-with-transparent-background.png +0 -0
  82. {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/images/babeldoc-big-logo-with-transparent-background.svg +0 -0
  83. {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/images/babeldoc-big-logo.png +0 -0
  84. {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/images/babeldoc-contributor_reward_example.png +0 -0
  85. {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/images/babeldoc-preview.gif +0 -0
  86. {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/images/babeldoc-small-logo-with-transparent-background.png +0 -0
  87. {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/images/babeldoc-small-logo-with-transparent-background.svg +0 -0
  88. {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/images/babeldoc-small-logo.png +0 -0
  89. {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/index.md +0 -0
  90. {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/intro-to-pdf-object.md +0 -0
  91. {babeldoc-0.2.7 → babeldoc-0.2.9}/docs/requirements.txt +0 -0
  92. {babeldoc-0.2.7 → babeldoc-0.2.9}/mkdocs.yml +0 -0
  93. {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.7
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"
@@ -3,7 +3,7 @@ import shutil
3
3
  import subprocess
4
4
  from pathlib import Path
5
5
 
6
- __version__ = "0.2.7"
6
+ __version__ = "0.2.9"
7
7
 
8
8
  CACHE_FOLDER = Path.home() / ".cache" / "babeldoc"
9
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=True timeout after {timeout} seconds, terminating subprocess"
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 without clean=True")
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=True completed successfully")
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=True failed with exit code {exit_code} or produced empty file"
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
- # Try to find the first title paragraph
77
- title_paragraph = self.find_title_paragraph(docs)
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
- local_title_paragraph = paragraph
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
- title_paragraph,
159
- local_title_paragraph,
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
- title_paragraph,
173
- local_title_paragraph,
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
@@ -528,6 +528,7 @@ class StylesAndFormulas:
528
528
  r"|NimbusRomNo9L.+"
529
529
  r"|GlosaMath.+"
530
530
  r"|URWPalladioL.+"
531
+ r"|CMSS.+"
531
532
  r")$"
532
533
  )
533
534
  if self.translation_config.formular_font_pattern:
@@ -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
- xreflen - 1 + len(font_list) + len(il.page) + len(font_list),
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
- import onnx
10
- import onnxruntime
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 = min(batch_size, max_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 = int(max_height / 32) * 32
236
+ target_imgsz = 1024
241
237
 
242
238
  # Preprocess batch
243
239
  processed_batch = []