BabelDOC 0.1.25__tar.gz → 0.1.27__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 (91) hide show
  1. {babeldoc-0.1.25 → babeldoc-0.1.27}/PKG-INFO +7 -2
  2. {babeldoc-0.1.25 → babeldoc-0.1.27}/README.md +6 -1
  3. babeldoc-0.1.27/babeldoc/__init__.py +1 -0
  4. {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/const.py +1 -1
  5. {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/backend/pdf_creater.py +6 -2
  6. {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/midend/il_translator.py +2 -0
  7. {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/midend/layout_parser.py +33 -61
  8. {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/midend/paragraph_finder.py +2 -0
  9. {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/midend/typesetting.py +6 -1
  10. {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/translator/translator.py +50 -0
  11. {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/docvision/doclayout.py +55 -13
  12. babeldoc-0.1.27/babeldoc/docvision/rpc_doclayout.py +304 -0
  13. {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/high_level.py +107 -0
  14. {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/main.py +49 -8
  15. {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/progress_monitor.py +33 -0
  16. {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/translation_config.py +54 -0
  17. {babeldoc-0.1.25 → babeldoc-0.1.27}/pyproject.toml +2 -2
  18. {babeldoc-0.1.25 → babeldoc-0.1.27}/uv.lock +1 -1
  19. babeldoc-0.1.25/babeldoc/__init__.py +0 -1
  20. babeldoc-0.1.25/babeldoc/docvision/rpc_doclayout.py +0 -176
  21. {babeldoc-0.1.25 → babeldoc-0.1.27}/.cursorignore +0 -0
  22. {babeldoc-0.1.25 → babeldoc-0.1.27}/.github/ISSUE_TEMPLATE/bug_report.yaml +0 -0
  23. {babeldoc-0.1.25 → babeldoc-0.1.27}/.github/ISSUE_TEMPLATE/feature_request.yaml +0 -0
  24. {babeldoc-0.1.25 → babeldoc-0.1.27}/.github/dependabot.yml +0 -0
  25. {babeldoc-0.1.25 → babeldoc-0.1.27}/.github/labels.yml +0 -0
  26. {babeldoc-0.1.25 → babeldoc-0.1.27}/.github/release-drafter.yml +0 -0
  27. {babeldoc-0.1.25 → babeldoc-0.1.27}/.github/workflows/codeql.yml +0 -0
  28. {babeldoc-0.1.25 → babeldoc-0.1.27}/.github/workflows/docs.yml +0 -0
  29. {babeldoc-0.1.25 → babeldoc-0.1.27}/.github/workflows/labeler.yml +0 -0
  30. {babeldoc-0.1.25 → babeldoc-0.1.27}/.github/workflows/publish-to-pypi.yml +0 -0
  31. {babeldoc-0.1.25 → babeldoc-0.1.27}/.github/workflows/test.yml +0 -0
  32. {babeldoc-0.1.25 → babeldoc-0.1.27}/.gitignore +0 -0
  33. {babeldoc-0.1.25 → babeldoc-0.1.27}/.pre-commit-config.yaml +0 -0
  34. {babeldoc-0.1.25 → babeldoc-0.1.27}/LICENSE +0 -0
  35. {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/assets/assets.py +0 -0
  36. {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/assets/embedding_assets_metadata.py +0 -0
  37. {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/asynchronize/__init__.py +0 -0
  38. {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/converter.py +0 -0
  39. {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/__init__.py +0 -0
  40. {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/babeldoc_exception/BabelDOCException.py +0 -0
  41. {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/backend/__init__.py +0 -0
  42. {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/frontend/__init__.py +0 -0
  43. {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/frontend/il_creater.py +0 -0
  44. {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/il_version_1.py +0 -0
  45. {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/il_version_1.rnc +0 -0
  46. {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/il_version_1.rng +0 -0
  47. {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/il_version_1.xsd +0 -0
  48. {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/midend/__init__.py +0 -0
  49. {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/midend/add_debug_information.py +0 -0
  50. {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/midend/detect_scanned_file.py +0 -0
  51. {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/midend/remove_descent.py +0 -0
  52. {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/midend/styles_and_formulas.py +0 -0
  53. {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/translator/__init__.py +0 -0
  54. {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/translator/cache.py +0 -0
  55. {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/utils/fontmap.py +0 -0
  56. {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/utils/layout_helper.py +0 -0
  57. {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/utils/style_helper.py +0 -0
  58. {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/xml_converter.py +0 -0
  59. {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/docvision/README.md +0 -0
  60. {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/docvision/__init__.py +0 -0
  61. {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/pdfinterp.py +0 -0
  62. {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/tools/generate_font_metadata.py +0 -0
  63. {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/CODE_OF_CONDUCT.md +0 -0
  64. {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/CONTRIBUTING.md +0 -0
  65. {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/CONTRIBUTOR_REWARD.md +0 -0
  66. {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/ImplementationDetails/AsyncTranslate/AsyncTranslate.md +0 -0
  67. {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/ImplementationDetails/ILTranslator/ILTranslator.md +0 -0
  68. {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/ImplementationDetails/PDFCreation/PDFCreation.md +0 -0
  69. {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/ImplementationDetails/PDFParsing/PDFParsing.md +0 -0
  70. {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/ImplementationDetails/ParagraphFinding/ParagraphFinding.md +0 -0
  71. {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/ImplementationDetails/README.md +0 -0
  72. {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/ImplementationDetails/StylesAndFormulas/StylesAndFormulas.md +0 -0
  73. {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/ImplementationDetails/Typesetting/Typesetting.md +0 -0
  74. {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/README.md +0 -0
  75. {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/deploy.sh +0 -0
  76. {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/images/babeldoc-banner.png +0 -0
  77. {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/images/babeldoc-big-logo-darkmode-with-transparent-background.png +0 -0
  78. {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/images/babeldoc-big-logo-darkmode-with-transparent-background.svg +0 -0
  79. {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/images/babeldoc-big-logo-with-transparent-background.png +0 -0
  80. {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/images/babeldoc-big-logo-with-transparent-background.svg +0 -0
  81. {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/images/babeldoc-big-logo.png +0 -0
  82. {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/images/babeldoc-contributor_reward_example.png +0 -0
  83. {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/images/babeldoc-preview.gif +0 -0
  84. {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/images/babeldoc-small-logo-with-transparent-background.png +0 -0
  85. {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/images/babeldoc-small-logo-with-transparent-background.svg +0 -0
  86. {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/images/babeldoc-small-logo.png +0 -0
  87. {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/index.md +0 -0
  88. {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/intro-to-pdf-object.md +0 -0
  89. {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/requirements.txt +0 -0
  90. {babeldoc-0.1.25 → babeldoc-0.1.27}/mkdocs.yml +0 -0
  91. {babeldoc-0.1.25 → babeldoc-0.1.27}/tests/test_translation_config.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: BabelDOC
3
- Version: 0.1.25
3
+ Version: 0.1.27
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
@@ -65,7 +65,8 @@ Description-Content-Type: text/markdown
65
65
 
66
66
  PDF scientific paper translation and bilingual comparison library.
67
67
 
68
- - Beta version launched [Immersive Translate - BabelDOC](https://app.immersivetranslate.com/babel-doc/) 1000 free pages per month.
68
+ - **Online Service**: Beta version launched [Immersive Translate - BabelDOC](https://app.immersivetranslate.com/babel-doc/) 1000 free pages per month.
69
+ - **Self-deployment**: [PDFMathTranslate](https://github.com/Byaidu/PDFMathTranslate) 1.9.3+ Experimental support for BabelDOC, available for self-deployment + WebUI with more translation services.
69
70
  - Provides a simple [command line interface](#getting-started).
70
71
  - Provides a [Python API](#python-api).
71
72
  - Mainly designed to be embedded into other programs, but can also be used directly for simple translation tasks.
@@ -157,6 +158,8 @@ uv run babeldoc --bing --files example.pdf --files example2.pdf
157
158
  - `--disable-rich-text-translate`: Disable rich text translation (may help improve compatibility with some PDFs)
158
159
  - `--enhance-compatibility`: Enable all compatibility enhancement options (equivalent to --skip-clean --dual-translate-first --disable-rich-text-translate)
159
160
  - `--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.
161
+ - `--watermark-output-mode`: Control watermark output mode: 'watermarked' (default) adds watermark to translated PDF, 'no_watermark' doesn't add watermark, 'both' outputs both versions.
162
+ - `--no-watermark`: [DEPRECATED] Use --watermark-output-mode=no_watermark instead.
160
163
 
161
164
  > [!TIP]
162
165
  > - Both `--skip-clean` and `--dual-translate-first` may help improve compatibility with some PDF readers
@@ -262,6 +265,8 @@ skip-clean = false
262
265
  dual-translate-first = false
263
266
  disable-rich-text-translate = false
264
267
  use-alternating-pages-dual = false
268
+ watermark-output-mode = "watermarked" # Choices: "watermarked", "no_watermark", "both"
269
+ # no-watermark = false # DEPRECATED: Use watermark-output-mode instead
265
270
 
266
271
  # Output control
267
272
  no-dual = false
@@ -30,7 +30,8 @@
30
30
 
31
31
  PDF scientific paper translation and bilingual comparison library.
32
32
 
33
- - Beta version launched [Immersive Translate - BabelDOC](https://app.immersivetranslate.com/babel-doc/) 1000 free pages per month.
33
+ - **Online Service**: Beta version launched [Immersive Translate - BabelDOC](https://app.immersivetranslate.com/babel-doc/) 1000 free pages per month.
34
+ - **Self-deployment**: [PDFMathTranslate](https://github.com/Byaidu/PDFMathTranslate) 1.9.3+ Experimental support for BabelDOC, available for self-deployment + WebUI with more translation services.
34
35
  - Provides a simple [command line interface](#getting-started).
35
36
  - Provides a [Python API](#python-api).
36
37
  - Mainly designed to be embedded into other programs, but can also be used directly for simple translation tasks.
@@ -122,6 +123,8 @@ uv run babeldoc --bing --files example.pdf --files example2.pdf
122
123
  - `--disable-rich-text-translate`: Disable rich text translation (may help improve compatibility with some PDFs)
123
124
  - `--enhance-compatibility`: Enable all compatibility enhancement options (equivalent to --skip-clean --dual-translate-first --disable-rich-text-translate)
124
125
  - `--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.
126
+ - `--watermark-output-mode`: Control watermark output mode: 'watermarked' (default) adds watermark to translated PDF, 'no_watermark' doesn't add watermark, 'both' outputs both versions.
127
+ - `--no-watermark`: [DEPRECATED] Use --watermark-output-mode=no_watermark instead.
125
128
 
126
129
  > [!TIP]
127
130
  > - Both `--skip-clean` and `--dual-translate-first` may help improve compatibility with some PDF readers
@@ -227,6 +230,8 @@ skip-clean = false
227
230
  dual-translate-first = false
228
231
  disable-rich-text-translate = false
229
232
  use-alternating-pages-dual = false
233
+ watermark-output-mode = "watermarked" # Choices: "watermarked", "no_watermark", "both"
234
+ # no-watermark = false # DEPRECATED: Use watermark-output-mode instead
230
235
 
231
236
  # Output control
232
237
  no-dual = false
@@ -0,0 +1 @@
1
+ __version__ = "0.1.27"
@@ -2,7 +2,7 @@ import shutil
2
2
  import subprocess
3
3
  from pathlib import Path
4
4
 
5
- __version__ = "0.1.25"
5
+ __version__ = "0.1.27"
6
6
 
7
7
  CACHE_FOLDER = Path.home() / ".cache" / "babeldoc"
8
8
 
@@ -12,6 +12,7 @@ from babeldoc.document_il import il_version_1
12
12
  from babeldoc.document_il.utils.fontmap import FontMapper
13
13
  from babeldoc.translation_config import TranslateResult
14
14
  from babeldoc.translation_config import TranslationConfig
15
+ from babeldoc.translation_config import WatermarkOutputMode
15
16
 
16
17
  logger = logging.getLogger(__name__)
17
18
 
@@ -416,8 +417,9 @@ class PDFCreater:
416
417
  pdf = self.subset_fonts_in_subprocess(pdf, translation_config, tag="debug")
417
418
  return pdf
418
419
 
420
+ @staticmethod
419
421
  def subset_fonts_in_subprocess(
420
- self, pdf: pymupdf.Document, translation_config: TranslationConfig, tag: str
422
+ pdf: pymupdf.Document, translation_config: TranslationConfig, tag: str
421
423
  ) -> pymupdf.Document:
422
424
  """Run font subsetting in a subprocess with timeout.
423
425
 
@@ -489,8 +491,8 @@ class PDFCreater:
489
491
  )
490
492
  return original_pdf
491
493
 
494
+ @staticmethod
492
495
  def save_pdf_with_timeout(
493
- self,
494
496
  pdf: pymupdf.Document,
495
497
  output_path: str,
496
498
  translation_config: TranslationConfig,
@@ -634,6 +636,8 @@ class PDFCreater:
634
636
  def write(self, translation_config: TranslationConfig) -> TranslateResult:
635
637
  basename = Path(translation_config.input_file).stem
636
638
  debug_suffix = ".debug" if translation_config.debug else ""
639
+ if translation_config.watermark_output_mode != WatermarkOutputMode.Watermarked:
640
+ debug_suffix += ".no_watermark"
637
641
  mono_out_path = translation_config.get_output_file_path(
638
642
  f"{basename}{debug_suffix}.{translation_config.lang_out}.mono.pdf",
639
643
  )
@@ -1,6 +1,7 @@
1
1
  import concurrent.futures
2
2
  import json
3
3
  import logging
4
+ import re
4
5
  from pathlib import Path
5
6
 
6
7
  from tqdm import tqdm
@@ -555,6 +556,7 @@ class ILTranslator:
555
556
  return
556
557
 
557
558
  translated_text = self.translate_engine.translate(text)
559
+ translated_text = re.sub(r"[. 。…]{20,}", ".", translated_text)
558
560
 
559
561
  tracker.set_output(translated_text)
560
562
 
@@ -110,71 +110,43 @@ class LayoutParser:
110
110
  def process(self, docs: il_version_1.Document, mupdf_doc: Document):
111
111
  """Generate layouts for all pages that need to be translated."""
112
112
  # Get pages that need to be translated
113
- pages_to_translate = [
114
- page
115
- for page in docs.page
116
- if self.translation_config.should_translate_page(page.page_number + 1)
117
- ]
118
- total = len(pages_to_translate)
113
+ total = len(docs.page)
119
114
  with self.translation_config.progress_monitor.stage_start(
120
115
  self.stage_name,
121
116
  total,
122
117
  ) as progress:
123
- # Process pages in batches
124
- batch_size = 1
125
- for i in range(0, total, batch_size):
126
- self.translation_config.raise_if_cancelled()
127
- batch_pages = pages_to_translate[i : i + batch_size]
128
-
129
- # Prepare batch images
130
- batch_images = []
131
- for page in batch_pages:
132
- pix = mupdf_doc[page.page_number].get_pixmap(dpi=72)
133
- image = np.fromstring(pix.samples, np.uint8).reshape(
134
- pix.height,
135
- pix.width,
136
- 3,
137
- )[:, :, ::-1]
138
- batch_images.append(image)
139
-
140
- # Get predictions for the batch
141
- layouts_batch = self.model.predict(batch_images, batch_size=batch_size)
142
-
143
- # Process predictions for each page
144
- for page, layouts in zip(batch_pages, layouts_batch, strict=False):
145
- page_layouts = []
146
- self._save_debug_image(
147
- batch_images[batch_pages.index(page)],
148
- layouts,
149
- page.page_number + 1,
118
+ # Process predictions for each page
119
+ for page, layouts in self.model.handle_document(
120
+ docs.page, mupdf_doc, self.translation_config, self._save_debug_image
121
+ ):
122
+ page_layouts = []
123
+ for layout in layouts.boxes:
124
+ # Convert coordinate system from picture to il
125
+ # system to the il coordinate system
126
+ x0, y0, x1, y1 = layout.xyxy
127
+ pix = mupdf_doc[page.page_number].get_pixmap()
128
+ h, w = pix.height, pix.width
129
+ x0, y0, x1, y1 = (
130
+ np.clip(int(x0 - 1), 0, w - 1),
131
+ np.clip(int(h - y1 - 1), 0, h - 1),
132
+ np.clip(int(x1 + 1), 0, w - 1),
133
+ np.clip(int(h - y0 + 1), 0, h - 1),
150
134
  )
151
- for layout in layouts.boxes:
152
- # Convert coordinate system from picture to il
153
- # system to the il coordinate system
154
- x0, y0, x1, y1 = layout.xyxy
155
- pix = mupdf_doc[page.page_number].get_pixmap()
156
- h, w = pix.height, pix.width
157
- x0, y0, x1, y1 = (
158
- np.clip(int(x0 - 1), 0, w - 1),
159
- np.clip(int(h - y1 - 1), 0, h - 1),
160
- np.clip(int(x1 + 1), 0, w - 1),
161
- np.clip(int(h - y0 + 1), 0, h - 1),
162
- )
163
- page_layout = il_version_1.PageLayout(
164
- id=len(page_layouts) + 1,
165
- box=il_version_1.Box(
166
- x0.item(),
167
- y0.item(),
168
- x1.item(),
169
- y1.item(),
170
- ),
171
- conf=layout.conf.item(),
172
- class_name=layouts.names[layout.cls],
173
- )
174
- page_layouts.append(page_layout)
135
+ page_layout = il_version_1.PageLayout(
136
+ id=len(page_layouts) + 1,
137
+ box=il_version_1.Box(
138
+ x0.item(),
139
+ y0.item(),
140
+ x1.item(),
141
+ y1.item(),
142
+ ),
143
+ conf=layout.conf.item(),
144
+ class_name=layouts.names[layout.cls],
145
+ )
146
+ page_layouts.append(page_layout)
175
147
 
176
- page.page_layout = page_layouts
177
- self._save_debug_box_to_page(page)
178
- progress.advance(1)
148
+ page.page_layout = page_layouts
149
+ self._save_debug_box_to_page(page)
150
+ progress.advance(1)
179
151
 
180
- return docs
152
+ return docs
@@ -244,6 +244,7 @@ class ParagraphFinder:
244
244
  def is_text_layout(self, layout: Layout):
245
245
  return layout is not None and layout.name in [
246
246
  "plain text",
247
+ "tiny text",
247
248
  "title",
248
249
  "abandon",
249
250
  "figure_caption",
@@ -312,6 +313,7 @@ class ParagraphFinder:
312
313
  "figure_caption",
313
314
  "abandon",
314
315
  "plain text",
316
+ "tiny text",
315
317
  "title",
316
318
  ]
317
319
  char_box = char.box
@@ -15,6 +15,7 @@ from babeldoc.document_il import PdfStyle
15
15
  from babeldoc.document_il import il_version_1
16
16
  from babeldoc.document_il.utils.fontmap import FontMapper
17
17
  from babeldoc.translation_config import TranslationConfig
18
+ from babeldoc.translation_config import WatermarkOutputMode
18
19
 
19
20
  logger = logging.getLogger(__name__)
20
21
 
@@ -450,7 +451,11 @@ class Typesetting:
450
451
  fonts[xobj.xobj_id] = page_fonts.copy()
451
452
  for font in xobj.pdf_font:
452
453
  fonts[xobj.xobj_id][font.font_id] = font
453
- if page.page_number == 0:
454
+ if (
455
+ page.page_number == 0
456
+ and self.translation_config.watermark_output_mode
457
+ == WatermarkOutputMode.Watermarked
458
+ ):
454
459
  self.add_watermark(page)
455
460
  # 开始实际的渲染过程
456
461
  for paragraph in page.pdf_paragraph:
@@ -8,6 +8,7 @@ import unicodedata
8
8
  from abc import ABC
9
9
  from abc import abstractmethod
10
10
 
11
+ import httpx
11
12
  import openai
12
13
  import requests
13
14
 
@@ -271,3 +272,52 @@ class OpenAITranslator(BaseTranslator):
271
272
 
272
273
  def get_rich_text_right_placeholder(self, placeholder_id: int):
273
274
  return self.get_formular_placeholder(placeholder_id + 1)
275
+
276
+
277
+ class TranslateTranslator(BaseTranslator):
278
+ # https://github.com/openai/openai-python
279
+ name = "openai"
280
+
281
+ def __init__(
282
+ self,
283
+ lang_in,
284
+ lang_out,
285
+ url=None,
286
+ ignore_cache=False,
287
+ ):
288
+ super().__init__(lang_in, lang_out, ignore_cache)
289
+ self.client = httpx.Client()
290
+ self.url = url
291
+
292
+ def do_translate(self, text) -> str:
293
+ response = self.client.post(
294
+ self.url,
295
+ json={
296
+ "text": [text],
297
+ "src": "Englih",
298
+ "tgt": "Simplifed Chinese",
299
+ },
300
+ )
301
+ return response.json()["text"][0]
302
+
303
+ def prompt(self, text):
304
+ return [
305
+ {
306
+ "role": "system",
307
+ "content": "You are a professional,authentic machine translation engine.",
308
+ },
309
+ {
310
+ "role": "user",
311
+ "content": f";; Treat next line as plain text input and translate it into {self.lang_out}, output translation ONLY. If translation is unnecessary (e.g. proper nouns, codes, {'{{1}}, etc. '}), return the original text. NO explanations. NO notes. Input:\n\n{text}",
312
+ },
313
+ ]
314
+
315
+ def get_formular_placeholder(self, placeholder_id: int):
316
+ return "{{v" + str(placeholder_id) + "}}"
317
+ return "{{" + str(placeholder_id) + "}}"
318
+
319
+ def get_rich_text_left_placeholder(self, placeholder_id: int):
320
+ return self.get_formular_placeholder(placeholder_id)
321
+
322
+ def get_rich_text_right_placeholder(self, placeholder_id: int):
323
+ return self.get_formular_placeholder(placeholder_id + 1)
@@ -2,12 +2,15 @@ import abc
2
2
  import ast
3
3
  import logging
4
4
  import platform
5
+ from collections.abc import Generator
5
6
 
6
7
  import cv2
7
8
  import numpy as np
8
9
  import onnx
9
10
  import onnxruntime
11
+ import pymupdf
10
12
 
13
+ import babeldoc.document_il.il_version_1
11
14
  from babeldoc.assets.assets import get_doclayout_onnx_model_path
12
15
 
13
16
  # from huggingface_hub import hf_hub_download
@@ -15,6 +18,19 @@ from babeldoc.assets.assets import get_doclayout_onnx_model_path
15
18
  logger = logging.getLogger(__name__)
16
19
 
17
20
 
21
+ class YoloResult:
22
+ """Helper class to store detection results from ONNX model."""
23
+
24
+ def __init__(self, names, boxes=None, boxes_data=None):
25
+ if boxes is not None:
26
+ self.boxes = boxes
27
+ else:
28
+ assert boxes_data is not None
29
+ self.boxes = [YoloBox(data=d) for d in boxes_data]
30
+ self.boxes.sort(key=lambda x: x.conf, reverse=True)
31
+ self.names = names
32
+
33
+
18
34
  class DocLayoutModel(abc.ABC):
19
35
  @staticmethod
20
36
  def load_onnx():
@@ -42,18 +58,19 @@ class DocLayoutModel(abc.ABC):
42
58
  **kwargs: Additional arguments.
43
59
  """
44
60
 
45
-
46
- class YoloResult:
47
- """Helper class to store detection results from ONNX model."""
48
-
49
- def __init__(self, names, boxes=None, boxes_data=None):
50
- if boxes is not None:
51
- self.boxes = boxes
52
- else:
53
- assert boxes_data is not None
54
- self.boxes = [YoloBox(data=d) for d in boxes_data]
55
- self.boxes.sort(key=lambda x: x.conf, reverse=True)
56
- self.names = names
61
+ @abc.abstractmethod
62
+ def handle_document(
63
+ self,
64
+ pages: list[babeldoc.document_il.il_version_1.Page],
65
+ mupdf_doc: pymupdf.Document,
66
+ translate_config,
67
+ save_debug_image,
68
+ ) -> Generator[
69
+ tuple[babeldoc.document_il.il_version_1.Page, YoloResult], None, None
70
+ ]:
71
+ """
72
+ Handle a document.
73
+ """
57
74
 
58
75
 
59
76
  class YoloBox:
@@ -192,7 +209,7 @@ class OnnxModel(DocLayoutModel):
192
209
  boxes[..., :4] = (boxes[..., :4] - [pad_x, pad_y, pad_x, pad_y]) / gain
193
210
  return boxes
194
211
 
195
- def predict(self, image, imgsz=1024, batch_size=16, **kwargs):
212
+ def predict(self, image, imgsz=800, batch_size=16, **kwargs):
196
213
  """
197
214
  Predict the layout of document pages.
198
215
 
@@ -254,3 +271,28 @@ class OnnxModel(DocLayoutModel):
254
271
  results.append(YoloResult(boxes_data=preds, names=self._names))
255
272
 
256
273
  return results
274
+
275
+ def handle_document(
276
+ self,
277
+ pages: list[babeldoc.document_il.il_version_1.Page],
278
+ mupdf_doc: pymupdf.Document,
279
+ translate_config,
280
+ save_debug_image,
281
+ ) -> Generator[
282
+ tuple[babeldoc.document_il.il_version_1.Page, YoloResult], None, None
283
+ ]:
284
+ for page in pages:
285
+ translate_config.raise_if_cancelled()
286
+ pix = mupdf_doc[page.page_number].get_pixmap(dpi=72)
287
+ image = np.fromstring(pix.samples, np.uint8).reshape(
288
+ pix.height,
289
+ pix.width,
290
+ 3,
291
+ )[:, :, ::-1]
292
+ predict_result = self.predict(image)[0]
293
+ save_debug_image(
294
+ image,
295
+ predict_result,
296
+ page.page_number + 1,
297
+ )
298
+ yield page, predict_result